Java Type Erasure: Rules of cast insertion?

2019-09-05 15:36发布

问题:

The Java tutorial on type erasure doesn't seem to detail the specific rules of cast insertion by the compiler. Can someone please explain the specific rules that cause the transformation detailed by the tutorial (reproduced below):

public class Node<T> {
    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.

Specifically, I'm wondering what rules cause the insertion of (MyNode) and (String). When is a cast inserted, and how is the type chosen for the cast?

回答1:

  1. MyNode mn = new MyNode(5);

    • will create an instance of MyNode which defines the generic type T of interface Node as Integer
    • casting: no casting necessary by developer, no casts added by compiler
  2. Node n = (MyNode)mn;

    • this will basically tell the compiler forget about the generic type T and use the interface Node completely without generics which will have the following consequence: imagine generic type T to be treated as java.lang.Object
    • casting: no casting necessary by developer, no casts added by compiler
  3. n.setData("Hello");

    • will allow you to add any kind ob object because T is treated as Object (String, Integer, array, anything else)
    • casting: no casting necessary by developer, no casts added by compiler
  4. Integer x = mn.data;

    • nm.data should return an Integer type as Integer is defined as generic type argument T in the MyNode class
    • however because you used raw types which allowed you to add a String instead, the nm.data holds a String instance
    • casting: no casting necessary by developer, however the compiler will add casts to Integer behind the scenes for you and because of type mismatch, you will get the ClassCastException


回答2:

The ClassCastException would be throw when a call is made to

n.setData("Hello");

This is because the compiler builds bridge methods to preserve polymorphism. The bridge method will look like:

 public void setData(Object data) {
       setData((Integer)data);   //the exception is thrown here
  }

since an instance of a string cannot be converted to Integer, the ClassCastException would be thrown.

You can read about bridge methods here.