Java Generics Quirks

Tavian Barnes

Can you guess which of these statements are valid Java 7? I know I can't! :)

Hint: Eclipse, javac, and the JLS disagree on these, so test-compiling won't help you.

abstract class ListOfListOf<T> implements List<List<T>> {
}

class Quirks {
    static void quirks(List<List<Number>> list) {
        // Easy one to warm up with
        List<List<? extends Number>> warmUp = list;

        // These casts are type-safe
        ListOfListOf<Number> normalCast = (ListOfListOf<Number>) list;
        ListOfListOf<?> wildcardCast = (ListOfListOf<?>) list;

        // So are these ones
        List<? extends List<? extends Number>> wider = list;
        ListOfListOf<?> narrowingCast = (ListOfListOf<?>) wider;
    }
}

Answers:

  • List<List<? extends Number>> does not capture a List<List<Number>>. In fact, it does not capture at all, so the assignment warmUp = list is invalid.
  • ListOfListOf<Number> normalCast = (ListOfListOf<Number>) list;
    Eclipse says yes, javac says yes.
  • ListOfListOf<?> wildcardCast = (ListOfListOf<?>) list;
    Eclipse says "no way man!" javac says "sure."
  • List<? extends List<? extends Number>> wider = list;
    Just a normal widening conversion. Eclipse and javac say "whatever, man."
  • ListOfListOf<?> narrowingCast = (ListOfListOf<?>) wider;
    Eclipse says "no problem," javac says "I'm sorry, Dave. I'm afraid I can't do that."

Ready to go again?

class CanYouInferT<T extends Comparable<? super T>> {
}

class Quirks {
    static <T extends Comparable<? super T>> void canYouInferT() {
    }

    static void quirks() {
        // Note that Object is not Comparable, so what is T?
        canYouInferT();
        CanYouInferT<?> canYouInferT = new CanYouInferT<>();
    }
}

Answers: Eclipse is perfectly happy to infer, um, something for T in both cases. javac chokes on new CanYouInferT<>(), but somehow still manages to infer something for the static call of canYouInferT().

Does anyone know what the spec says about these corner cases?