Potential issue with one of the Oracle traces in Java generics

I was looking through one of the Oracle trails in Java generics called " Effects like Erasure and Bridge Methods " and I could not convince myself of the explanation. Curiously, I tested the code locally and I could not reproduce the behavior that the trace explains. Here is the relevant code:

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); } } 

The Oracle trail claims the following behavior for this piece of code:

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

This piece of code should look like this after erasing the styles:

 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. 

I did not understand what techniques are used here or behavior. When I tried to run this code locally using IntelliJ with Java 7, I got this behavior:

 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; 

In other words, the JVM will not allow String to be used with setData() . It is really intuitive for me, and it is consistent with my understanding of generics. Since MyNode mn was constructed using Integer , the compiler must discard every call to setData() with Integer to ensure type safety (i.e. Integer ).

Can someone shed light on this obvious mistake in the Oracle trailer?

+7
java generics type-erasure
source share
2 answers

You are not reading the Oracle page correctly. If you read everything to the end, you will find that he is talking about what is happening, what you are describing.

This is not a well-written page; the author says that “blah happens” when they mean “IF IT WAS A MATTER, THAT was BLAW, BUT HOW WE WILL SHOW THAT NOT MATTER”. They are too free in their language.

The point of the page - bridge methods - is an explanation of how real behavior occurs as you observed when the predicted behavior (based on the design or development of generalized solutions) is what they "suggested" at the beginning.

+3
source share

Well, that was explained in the trail.

In theory, when the Node class is compiled, its base type T is erased to Object .

Thus, in fact, it is compiled into something like

 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; } } 

Then you create a child class MyNode , which has its own setData(Integer data) . As for Java, this is an overload of the setData method, not its override. Each MyNode object has two setData methods. One of them is setData(Object) inherited from Node , and the other is setData(Integer) .

Basically, if you use a raw type, and you call setData with any reference that is not an Integer , the usual Java interpretation for this would be to overload setData(Object) .

This will not cause an assignment problem, because data declared as Object , not as Integer . The problem will only be when you try to return data to an Integer link. This simple Java behavior will cause the MyNode be "polluted" with inappropriate data.

However, as the trail says, the compiler adds a bridge method to make the child class look more like you intuitively think about it. It adds an override from setData(Object) to MyNode , so you cannot call the original, unsafe Node.setData(Object) . This bridge override method has an explicit conversion to Integer , which ensures that you cannot assign a non-integer link to data .

And this is the behavior that you see when you really compile and run the example.

If you run javap -p in the MyNode.class file, you will see that it has two setData methods:

 class MyNode extends Node<java.lang.Integer> { public MyNode(java.lang.Integer); public void setData(java.lang.Integer); public void setData(java.lang.Object); } 
+1
source share

All Articles