Why isn't a conversion to "GenericType<?>" allowed here?

eclipse, generics, java

Solution

If a programmer wants to explicitly cast a type `X` to type `Y`, the language could just allow it, assuming that the programmer knows better than the compiler.

But the good guys at Java want to prevent some obviously impossible casts, e.g. `(Cat)dog`. So they have this glorious detailed section dedicated to the subject - http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

But, are there any problems with these rules? And, do compilers conform to these rules? ... These questions are too complex and not very interesting.

What we should care is whether the cast makes sense; if it does make sense, and the compiler refuses to accept it, no problem, just work around it.

In your question, there are two places to insert wildcards, in `Foo< >`, and in `Iterable< >`. In each place, the wildcard can be

    0. None
    1. Bounded    "? extends Something"
    2. Unbounded  "?"

so let's explore all combinations, and here's my conclusion:

     wildcard#1    wildcard#2     should_compile   javac     eclipse

00   -             -              Y                Y         N
01   -             ? extends      N                N         N
02   -             ?              N                N         N
10   ? extends     -              Y                N         Y
11   ? extends     ? extends      Y                N         Y
12   ? extends     ?              Y                Y         Y
20   ?             -              Y                Y         Y

`should_compile` means the cast makes sense or not, explained later.

In case `10 and 11`, code should compile, but javac rejects it. Either the rules have problems, or javac has bugs.

Let's see for example, why case `00` makes sense and should compile

void test00(Foo<Iterable<String>> foo) {
    Bar<?> bar = (Bar<?>) foo;
}

the question is, could there be a class/interface `X`, such that
      Bar<X> <: Foo<Iterable<String>> 
=>    Foo<Iterable<X>> <: Foo<Iterable<String>> 
=>    Iterable<X> = Iterable<String>
=>    X = String
so the answer is yes, the cast makes sense.

and why case `01` should not compile

     Foo<Iterable<X>> <: Foo<Iterable<? extends String>>
=>   Iterable<X> = Iterable<? extends String>
=>   NO SOLUTION
     note that Iterable<String> != Iterable<? extends String>

and case `11`

     Foo<Iterable<X>>  <: Foo<? extends Iterable<? extends String>>
=>   Iterable<X> <: Iterable<? extends String>
=>   X <: String

It is surprising that case `01` should not compile, although it feels sensible. The root problem is, `Iterable` is convariant, and we ought to use a wildcard with it almost anywhere it's used. So ideally we should declare

class Bar<T> implements Foo<Iterable<? extends T>>

but life is hell if we insert wildcards everywhere. The better solution is declaration site variance. Not sure if Java will ever add that feature before we all retire.

Problem

This code causes a compile error with javac (but, notably, not with Eclipse 4.2.2!): ``` public interface Foo<T> { } class Bar<T> implements Foo<Iterable<T>> { } class Test { void test(Foo<? extends Iterable<? extends String>> foo) { Bar<?> bar = (Bar<?>) foo; } } ``` The error from javac is this: ``` Foo.java:9: error: inconvertible types Bar<?> bar = (Bar<?>) foo; ^ required: Bar<?> found: Foo<CAP#1> where CAP#1 is a fresh type-variable: CAP#1 extends Iterable<? extends String> from capture of ? extends Iterable<? extends String> ``` Changing the cast to `(Bar) foo` (i.e. using the raw type) allows the code to compile, as does changing the type of `foo` to simply `Foo<? extends Iterable<?>>`. EDIT: Hilariously, this simple change causes Eclipse to reject, but javac to accept: ``` void test(Foo<Iterable<String>> foo) { Bar<?> bar = (Bar<?>) foo; } ``` And, both Eclipse and javac reject this one: ``` void test(Foo<Iterable<? extends String>> foo) { Bar<?> bar = (Bar<?>) foo; } ```

Original source

Related problems