No ClassCastException on wrong generic cast [Java]

stecrz :

Have a look at the main method of the following class:

public class Outer {
    static class A<T> {
        public A<T> a;
        public T t;

        public A() { a = null; }
        public A(T t) {
            a = (A<T>) new B();
            a.t = t;
        }
    }

    static class B extends A<Integer> {}

    public static void main(String[] args) {
        // A<String> c = new B();              // CREATION 1: would not work
        // A<String> b = (A<String>) new B();  // CREATION 2: would not work
        A<String> a1 = new A<>("str");         // CREATION 3: works (???)
        A<String> a2 = new A(0);               // CREATION 4: works (???)

        System.out.println(a1.a);    // CALL 1 -> Poly$B@...
        System.out.println(a1.t);    // CALL 2 -> null
        System.out.println(a1.a.a);  // CALL 3 -> null
        System.out.println(a1.a.t);  // CALL 4 -> str (???)
        System.out.println(a2.a.t);  // CALL 5 -> ClassCastException (???)
    }
}

Obviously, creation 1 and 2 would fail at compile time, since the cast cannot be possible. The 3rd object creation could be possible, so I did not expect it to fail at compile time, but: I expected (A<T>) new B(); to fail at runtime, since B extends A<Integer> and it should not be possible to cast A<Integer> to A<String> (same as creation 2)... But there is no ClassCastException here!?

Unfortunately, creation 3 works. a1.a.t (which is the t member of a B object) will now store the String "str"!? So a B object (meaning t is of type Integer) can store Strings in Integer t!!?

Another thing: Look at a2. Trying to access a2.a.t will produce a ClassCastException. This surprises me, since there is not even a cast performed during the access - at least, this is what I thought...

So my questions in short are:

  1. Why is creation 3 (/4) possible?
  2. Why is a B object able to store a String in its generic Integer variable t?
  3. Why does call 5 fail at runtime?

Thank you!! :)

Tom Hawtin - tackline :

Why is creation 3 (/4) possible?

Creation 3 Is the "diamond" syntax for inferring type. So new A<> is the same as writing new A<String>.

So we can replace that with the long, pre-Java 1.7 syntax.

A<String> a1 = new A<String>("str");

This will go through the following constructor, where T was java.lang.String and t is a java.lang.String. Note, there is just one copy of this code at runtime, with the raw type of T being java.lang.Object.

    public A(T t) {
        a = (A<T>) new B();
        //  ^^^^^^ NB: This will at least give a rawtype warning
        //             as it causes heap pollution.
        a.t = t;
    } 

The type of a is A<T> at compile time and java.lang.Object erased. The type of both a.t and t is T at compile time and java.lang.Object erased. So it compiles without warning and there is no casting to check at runtime. The assignment succeeds.

Creation 4 is using raw types. Your compiler should give a warning (use -Xlint). Ignoring these warning may lead to ClassCastException later on. Generics are a compiler "fiction" over the real virtual machine, which essentially follows the rules of the Java 1.0 language.

Why is a B object able to store a String in its generic Integer variable t?

The erased type of t in A (and hence B) is java.lang.Object and can therefore store any reference type if there is no or incorrect checking.

Why does call 5 fail at runtime?

The object at a2.a.t is a java.lang.Integer. a2.a has static type A<java.lang.String> so the static type of a2.a.t is java.lang.String. javac will insert a cast in order to check that the raw type returned conforms to the static type. In this particular case, IIRC, older versions of javac would incorrect omit the cast and use the println(java.lang.Object) overload instead of println(java.lang.String).

You can use javap -c -private to see exactly where checkcast instructions are placed.

Conclusion

Make sure you have warnings switched on. Anything which doesn't have them on by default is somewhat suspect. Don't ignore or suppress warnings, particularly about raw types.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=152036&siteId=1