Why does this give an unchecked cast warning?

Consider this example:

interface A<T> {}
interface B<T> extends A<T> {
    void run();
}
class Test {
    public void main(String[] args) {
        A<SomeType> a = ...;
        if (a instanceof B)
            ((B<SomeType>) a)    //warning: "Type safety: unchecked cast"
                .run();
    }
}

Why does that give an unchecked cast warning? Is there a way this cast could be dangerous, or can I simply ignore this warning?

Note, that I can't use if (a instanceof B<SomeType>), because that gives the following compiler error:

Cannot perform instanceof check against parameterized type B<SomeType>. Use the form B<?> instead since further generic type information will be erased at runtime

And, if I use if (a instanceof B<?>), that still gives the same warning.

Jon Skeet
people
quotationmark

When this happens normally, it means that the cast isn't really checking anything. Here's a concrete example:

import java.util.*;

public class Test {

    public static void main(String args[]) {
        List<String> strings = new ArrayList<String>();
        strings.add("Foo");

        Object o = strings;
        if (o instanceof List) {
            List<Integer> integers = (List<Integer>) o;
            System.out.println("The cast 'worked'");
            Integer firstInteger = integers.get(0); // ClassCastException
        }
    }
}

At execution time, there's no such thing as a List<Integer> - there's just a List which happens to contain Integer references, and a List which happens to contain String references, etc. So the only execution-time check a cast to List<Integer> performs is that it really is a List.

In your actual code, you had:

interface B<T> extends A { ... }

That's using the raw type... which means that just because you've got a reference to an A<Foo> which is also a B, it doesn't need to really be a B<Foo>. When you're using raw types, all kinds of bets are off :)

In the code you posted, with:

interface B<T> extends A<T> { ... }

you don't have a warning, because if you've genuinely got an A<Foo> which is a B, it must be a B<Foo>. (Of course, you could only have an A<Foo> via another unsafe cast, but that means the problem occurs earlier on.)

See the Java Generics FAQ on type erasure and on raw types for more details.

people

See more on this question at Stackoverflow