2

The following is a contrived, self-contained example I created to demonstrate a problem I'm having with a much more complicated program:

public class MyTest {
    public static void main(String[] args) {
        SubObject[] array = new SubObject[5];
        Iterator<? extends SuperObject> iterator 
                = Arrays.asList((SuperObject[]) array).iterator();
        Iterable<? extends SuperObject> iterable = () -> iterator;
    }
}

class SuperObject {};

class SubObject extends SuperObject{};

This does not compile:

MyTest.java:9: error: incompatible types: bad return type in lambda expression
        Iterable<? extends SuperObject> iterable = () -> iterator;
                                                         ^
    Iterator<CAP#1> cannot be converted to Iterator<SuperObject>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends SuperObject from capture of ? extends SuperObject

I know I could simplify the generics, replacing <? extends SuperObject> with <SuperObject>. But this would defeat my purpose in the larger program. Why this error?

6
  • May I just ask why having Iterator<SuperObject> and Iterable<SuperObject> would defeat the purpose of your program? Commented Jan 21, 2018 at 5:11
  • Consider a super container class and a set of sub container classes that extend the super container. The super container defines an abstract iterable() method that returns an Iterable<Widget>. Each sub container contains an array of a class that extends Widget, and implements iterable() to return an Iterable over the array. If the array in a sub container is implemented using ArrayList<ExtOfWidget>, I can't seem to create an Iterable that can be returned by iterable(), i.e. an Iterable<Widget>. Can't cast ArrayList<ExtOfWidget> to ArrayList<Widget>. Commented Jan 21, 2018 at 5:19
  • To continue: However it all works if iterable() returns an Iterable<? extends Widget>, at least except for the problem I present in this question. Commented Jan 21, 2018 at 5:21
  • Your example is a bit convoluted. You can demonstrate the same issue like this: Iterator<?> iterator = Collections.emptyIterator(); Iterable<?> iterable = () -> iterator; Commented Jan 21, 2018 at 5:53
  • Either way, the example serves to demonstrate the error, but not why it's actually a problem. Can you elaborate on your sample use case above so we can understand what you're really trying to do? Commented Jan 21, 2018 at 5:59

1 Answer 1

2

Thanks to @Radiodef's comment, I was able to figure this out. The trick, as explained in https://docs.oracle.com/javase/tutorial/java/generics/capture.html, is to use a helper function:

public class MyTest4 {
    public static void main(String[] args) {
        SubObject[] array = new SubObject[5];
        Iterator<? extends SuperObject> iterator
                = Arrays.asList((SuperObject[]) array).iterator();
        Iterable<? extends SuperObject> iterable = getIterable(iterator);
    }

    static <T> Iterable<T> getIterable(Iterator<T> iterator) {
        return () -> iterator;
    }

}

class SuperObject {};

class SubObject extends SuperObject{};
Sign up to request clarification or add additional context in comments.

3 Comments

Interesting observation. If one could write "lambdas with type-arguments" in Java, one could do the same thing inline: Iterator<? extends Blah> a = ...; Iterable<? extends Blah> b = (<T>(Iterator<T> iterator) -> (Iterable<T>)(() -> iterator)).apply(a);. This would be akin to "inline syntax for natural transformations from Iterator<-> to Iterable<->". Unfortunately, this isn't valid (yet). +1
Note that the type cast in the first statement is obsolete; you can simply do Iterator<? extends SuperObject> iterator = Arrays.asList(array).iterator();, but that does not imply that you should. As the same tutorial states: “Using a wildcard as a return type should be avoided because it forces programmers using the code to deal with wildcards”. In your example, no-one is forcing you, you are rather forcing yourself, but keep that in mind for whatever real life scenario you simplified to this example.
And just for records, you can do Iterable<? extends SuperObject> iterable = Arrays.<SuperObject>asList(array)::iterator; which would even be a semantically correct Iterable, is it isn’t just a one-time iteration only iterable. Still, there is no real reason to force having a wildcard in the variable…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.