Reputation: 403
I was wonder if it is possible for the generic type of one class to be defined by the generic type of another object being passed as a parameter?
What I'm working on is a thread safe iterator so multiple threads can safely iterator through a list with no two threads getting the same object. It's works well in it's current form but I think it can be done slightly better.
import java.util.Iterator;
public class AtomicIterator implements Iterator<Object>
{
private Iterator<?> it;
public AtomicIterator(Iterable<?> iterable)
{
it = iterable.iterator();
}
public Object next()
{
synchronized(it)
{
if(it.hasNext())
return it.next();
else
return null;
}
}
}
Some of the code has been omitted but this should get the idea across. Currently to get the next object you are always forced to cast the object returned which seems inefficient.
ArrayList<String> someList = new ArrayList<String>;
AtomicIterator it = new AtomicIterator(someList);
String example = (String)it.next();
The problem is clearly that it.next()
returns type Object
where I want, in this example, for it to return type String
The easy solution is to give AtomicIterator
it's own generic type resulting in something like so
ArrayList<String> someList = new ArrayList<String>();
AtomicIterator<String> it = new AtomicIterator<String>(someList);
String example = it.next();
However this seems redundant to me, someList
has had it's generic type explicitly defined as String
and what I want is for AtomicIterator
to infer it's generic type from the Iterable
object that was given to it.
What I really want is something like this
import java.util.Iterator;
public class AtomicIterator implements Iterator<E>
{
private Iterator<E> it;
public <E> AtomicIterator(Iterable<E> iterable)
{
it = iterable.iterator();
}
public E next()
{
synchronized(it)
{
if(it.hasNext())
return it.next();
else
return null;
}
}
}
And from there be able to do something like
ArrayList<String> someList = new ArrayList<String>();
AtomicIterator it = new AtomicIterator(someList);
String example = it.next();
But alas this doesn't work because the generic type E
only exists within the scope of the constructor.
Does anyone know a nice clean way to do this?
Upvotes: 4
Views: 566
Reputation: 19682
Java 7 Diamond inference -
AtomicIterator<String> it = new AtomicIterator<>(someList);
^
no need to repeat String here
public class AtomicIterator<E> implements Iterator<E>
public AtomicIterator(Iterable<E> iterable)
If you don't want to explicitly write the full type of it
, instead, let compiler to infer the type, e.g.
var it = new ...; // the type of `it` is inferred from right hand side
unfortunately Java doesn't have that feature yet, and won't have it for a while.
Upvotes: 0
Reputation: 178293
The problem with your generic AtomicIterator
is that the constructor is itself generic and has a generic type distinct from the class's generic type parameter. The <E>
in public <E> AtomicIterator
defines a different generic parameter than the class generic parameter, even though both are E
. During my initial attempt to compile it, I got the rather confusing error:
AtomicIterator.java:8: incompatible types
found : java.util.Iterator<E>
required: java.util.Iterator<E>
it = iterable.iterator();
^
Solution: Let the constructor's generic parameter Iterable<E>
use the class's generic parameter 'E' by removing the <E>
from right after public
and adding it to the class, so the scope of the generic type E
is the whole class:
public class AtomicIterator<E> implements Iterator<E>
// ^ Added here
...
public AtomicIterator(Iterable<E> iterable)
// ^ Remove here
Upvotes: 0
Reputation: 45586
Add generic type to AtomicIterator, and (if running under Java 6) static factory method, so it resolves generic types by default
public class AtomicIterator<T> implements Iterator<T>
{
private Iterator<T> it;
public AtomicIterator(Iterable<T> iterable)
{
it = iterable.iterator();
}
public static <T> AtomicIterator<T> create ( Iterable<T> iterable )
{
return new AtomicIterator( iterable )
}
public T next()
{
synchronized(it)
{
if(it.hasNext())
return it.next();
else
return null;
}
}
}
Here is the usage:
ArrayList<String> someList = new ArrayList<String>;
AtomicIterator<String> it = AtomicIterator.create(someList);
Upvotes: 6