user2572030
user2572030

Reputation: 230

Upper bounded wildcard, iterator and for-each loop

I don't understand why this code compiles:

public void test(Collection<? extends Enum> enumCollection) {
    for (Enum e : enumCollection) {
    }
}

and this one does not:

public void test(Collection<? extends Enum> enumCollection) {
    Iterator<Enum> iterator = enumCollection.iterator();
    //Required: Iterator<Java.lang.Enum>
    //Found: Iterator<capture<? extends Java.lang.Enum>>
}

? extends Enum should be a subtype of Enum in all cases, so why am I getting a compilation error with the iterator and why does it work with a for-each loop?

Upvotes: 4

Views: 718

Answers (2)

OldCurmudgeon
OldCurmudgeon

Reputation: 65811

I often find that the adage if you need to use ? in your generic you are probably doing something wrong applies.

This works fine:

public <T extends Enum<T>> void test(Collection<T> enumCollection) {
    for (Enum e : enumCollection) {
    }
    Iterator<T> iterator = enumCollection.iterator();
}

Your problem is that you are expecting generics to act like ordinary code where an ArrayList is also a List. It is not designed for that. Generics are to ensure that you are using the same type, not just some compatible type.

In your case - to match the ? extends Enum you could use:

    Iterator<?> iterator = enumCollection.iterator();

which works fine.

Your types do not match - and generics are there to make sure they do.

Remember that ? does not mean I don't care what type it is it means I don't want to know what type it is. Using ? in a generic type and then expecting to be able to make use of the type is just silly.

Upvotes: 8

Florent Bayle
Florent Bayle

Reputation: 11910

Concerning the for loop, according to the JLS 14.14.2, the following code:

public void test(Collection<? extends Enum> enumCollection) {
    for (Enum e : enumCollection) {
    }
}

Will be translated to something like this:

public <T extends Enum> void test(Collection<T> enumCollection) {
    for (Iterator<T> iterator = enumCollection.iterator(); iterator.hasNext();) {
        Enum e = (T) iterator.next();
    }
}

Which compiles without problem. As you can see, you will never have an Iterator<Enum> directly, but instead an Iterator of whatever you said is the type of the elements in your collection.

The relevant part of the JLS is:

If the type of Expression is a subtype of Iterable for some type argument X, then let I be the type java.util.Iterator; otherwise, let I be the raw type java.util.Iterator.

The storage in an Enum variable is only done for each element, where it's relevant.

Upvotes: 3

Related Questions