Chetan
Chetan

Reputation: 1151

Generics query about overloading the method

I was reading this example from JavaDocs In the first example the docs says it will call the setData(Object) method, but when I tried the code it never calls that method, instead it calls the setData(Integer data) method.

But when I implement this example it gives the runtime exception

java.lang.ClassCastException at n.setData("Hello");

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

But the doc says:

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.

ClassCast Exception will occur at 4th line. It doesn't happen that way.

Upvotes: 1

Views: 55

Answers (2)

Tagir Valeev
Tagir Valeev

Reputation: 100139

When you override the method with generic parameter and overridden method has more specific bound, the following thing occurs. In the basic class bytecode you have the method with erased signature:

Object data;

public void setData(Object data) {
    System.out.println("Node.setData");
    this.data = data;
}

As the subclass is not generic anymore, your method in bytecode is exactly the same as in source:

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

However as you can see this method does not override the superclass method as argument type is different. Nevertheless it must be overridden. To do this the java compiler generates a special synthetic bridge method in MyNode class:

public void setData(Object data) {
    this.setData((Integer)data);
}

So here's your cast which fails. When you call n.setData("Hello"), it's a virtual call and this synthetic method is called which, of course throws an exception.

You may think that the alternative solution would be not to introduce the bridge method, but during the compilation change the argument type in MyNode:

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

Such solution would introduce much more problems especially with code binary compatibility. The users of your MyNode should not know that your class extends something with some generic parameter, it's an implementation detail. They should be able just to call your setData with normal Integer parameter. You may later change the superclass to remove generic parameter in Node (assuming that all nodes are integers now) and the users of MyNode class should still work correctly without recompilation.

Upvotes: 1

afsantos
afsantos

Reputation: 5208

What happens is already explained in the same tutorial.

  • n.setData("Hello"); causes the method setData(Object) to be executed on the object of class MyNode. (The MyNode class inherited setData(Object) from Node.)
  • In the body of setData(Object), the data field of the object referenced by n is assigned to a String.
  • The data field of that same object, referenced via mn, can be accessed and is expected to be an integer (since mn is a MyNode which is a Node<Integer>.
  • Trying to assign a String to an Integer causes a ClassCastException from a cast inserted at the assignment by a Java compiler.

So, as you see, this is what you experienced. The method invocation causes the exception, when trying to assign a String to an Integer.

What's confusing about it is that the comment in the code is perhaps misplaced. It is trying to tell you that the code won't reach line 4 because an exception will be thrown by that point. However, it would be clearer if the comment was in line 3, where the exception is actually thrown.

Upvotes: 2

Related Questions