Michael Williams
Michael Williams

Reputation: 403

Can a generic type be inferred from the generic type of a constructor parameter?

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

Answers (3)

ZhongYu
ZhongYu

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

rgettman
rgettman

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

Alexander Pogrebnyak
Alexander Pogrebnyak

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

Related Questions