javagenericsiterator

Error on generic type within wildcard bound


I have a custom iterator interface

public interface ThrowingIterator<T, E extends Throwable> {
  // matches interface of standard Iterator, but next() and hasNext() can throw E
}

and an implementation

public class CustomIterator implements ThrowingIterator<List<MyType>, IOException> {
  // ...
  public CustomIterator() {
     // ...
  }
  public static ThrowingIterator<List<MyType>, IOException> helperFactory() {
    // ...
  }
}

where class MyType implements MyInterface. The issue that I have is within the following function:

ThrowingIterator<List<? extends MyInterface>, IOException> getIterator() {
  if (someCondition) {
    return new CustomIterator();
  }
  return CustomIterator.helperFactory();
}

The compiler is erroring on both return statements, for different reasons. Intellij provides the following:

// first
Required: ThrowingIterator<List<? extends MyInterface>, IOException>
Provided: CustomIterator
// second
Required: ThrowingIterator<List<? extends MyInterface>, IOException>
Provided: ThrowingIterator<List<MyType>, IOException>

As far as I can tell, CustomIterator should fit what is needed, since the type it implements for ThrowingIterator fits within the wildcard bounds (especially looking at the second case, since MyType implements MyInterface, which is what I thought is exactly what ? extends MyInterface is trying to capture.) How can this generics issue be resolved while minimizing the amount of wildcards in declarations/signatures? The signature of getIterator() is fine as-is. I also understand supplying an iterator over a list is bad practice; fixing that is handled elsewhere.


Solution

  • Your method getIterator doesn't compile, because by default Java's generics are invariant.

    What this means is that even though a List<MyType> is a subtype of List<? extends MyInterface>, a ThrowingIterator<List<MyType>, IOException> is not a subtype of ThrowingIterator<List<? extends MyInterface>, IOException>.

    This is for the same reason that List<MyType> is not a subtype of List<MyInterface> - Java's generics are invariant. You made that covariant already by adding ? extends, resulting in List<? extends MyInterface>. Now you just have to do that again, because you've added another layer of generics that is still invariant.

    Add ? extends again:

    //               vvvvvvvvv
    ThrowingIterator<? extends List<? extends MyInterface>, IOException> 
        getIterator() {
    

    That will satisfy IntelliJ and the Java compiler.