Effects of Type Erasure and Bridge Methods (The Java™ Tutorials Learning the Java Language (original) (raw)
Sometimes type erasure causes a situation that you may not have anticipated. The following example shows how this can occur. The following example shows how a compiler sometimes creates a synthetic method, which is called a bridge method, as part of the type erasure process.
Given the following two classes:
public class Node {
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 { public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
Consider the following code:
MyNode mn = new MyNode(5); Node n = mn; // A raw type - compiler throws an unchecked warning n.setData("Hello"); // Causes a ClassCastException to be thrown. Integer x = mn.data;
After type erasure, this code becomes:
MyNode mn = new MyNode(5); Node n = mn; // A raw type - compiler throws an unchecked warning // Note: This statement could instead be the following: // Node n = (Node)mn; // However, the compiler doesn't generate a cast because // it isn't required. n.setData("Hello"); // Causes a ClassCastException to be thrown. Integer x = (Integer)mn.data;
The next section explains why a ClassCastException
is thrown at the n.setData("Hello");
statement.
Bridge Methods
When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, which is called a bridge method, as part of the type erasure process. You normally don't need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.
After type erasure, the Node and MyNode classes become:
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
After type erasure, the method signatures do not match; theNode.setData(T) method becomes Node.setData(Object). As a result, the MyNode.setData(Integer) method does not override theNode.setData(Object) method.
To solve this problem and preserve thepolymorphism of generic types after type erasure, the Java compiler generates a bridge method to ensure that subtyping works as expected.
For the MyNode class, the compiler generates the following bridge method for setData:
class MyNode extends Node {
**// Bridge method generated by the compiler**
// public void setData(Object data) { setData((Integer) data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
The bridge method MyNode.setData(object)
delegates to the original MyNode.setData(Integer)
method. As a result, the n.setData("Hello");
statement calls the methodMyNode.setData(Object)
, and a ClassCastException
is thrown because "Hello"
can't be cast to Integer
.