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.