12

I am new to java.I still feel I have to understand a lot so if this question seems stupid forgive me. Now I was going through http://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html

Here I found a lot of confusion .

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

    public static void main(String[] args) {
        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;

    }
}

When I ran this code I got below error

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at MyNode.setData(MyNode.java:1)
at MyNode.main(MyNode.java:14)

Below are the confusions
1) Why is it showing line number 1
2) If you read through the blog they say that the type erasure will replace type parameter will replace above code as below

public class Node {

    private 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(data);
        super.setData(data);
    }

    public static void main(String[] args) {
        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

    }
}

when I run the above code I get no error , code runs fine

from the documentation both r same ? why this different in behavior

now other most important question on oop is when we extend one class and when we call super constructor though super object is not created then what is the use of calling super. Please explain.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
sumedha
  • 473
  • 1
  • 9
  • 24

3 Answers3

6

from the documentation both r same ? why this different in behavior

The reason there is different behavior between the two code samples is because when you replaced the generic T with Object, you indirectly caused setData() to no longer override setData() in the super class. Since the parameter types are different, you are simply overloading it in the second example. So in the second example you are calling the super class directly which takes Object. In the first example you are calling the subclass which takes Integer.

when we call super constructor though super object is not created then what is the use of calling super

The super class and the sub-class are the same object (with its code split between two classes). So the super class constructor is called to initialize any fields defined in the super class. In your example, if you never called super(data) then this.data would never have been set.

Ted Bigham
  • 4,237
  • 1
  • 26
  • 31
6

If you read through the blog they say that the type erasure will replace type parameter will replace above code as below

Yes, it is right. And that is when bridge method comes into action. What happens is, with that type erasure, the setData() method in the subclass is no longer an override-equivalent for superclass setData() method. So, the behaviour at compile time does not persist till runtime. To preserve the behaviour, the compiler internally generates a bridge method.

Bridge method is often generated by the compiler, when a type extends or implements a parameterized class or interface and type erasure changes the signature of any inherited method.

And here is how it works. The compiler generates the following method in the subclass:

// Bridge method
public void setData(Object data) {
    setData((Integer) data);
}

Now, this method overrides the setData(Object) method of super class. And as you notice, this method internally invokes the original method only, casting the data to Integer. This is where you get ClassCastException. The data is really of String type, which cannot be cast to Integer.

when I run the above code I get no error , code runs fine

As I said above, after type erasure, the setData() method in subclass doesn't overrides the one in super class method. So, when you invoke the method on Node reference, it will call the Node class method only, whose signature is:

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

And there won't be issue with this, because you are not casting anything. You are storing the String type data to an Object type reference. That is fine.

when we call super constructor though super object is not created then what is the use of calling super.

Well, that should have been a separate question altogether. Anyways, the point is, an object's state comprises of the data members declared in that class, and all it's super classes. A constructor in that particular class, will only initialize the state in that class. To initialize the state of the object in super class, you must chain the super class constructor.

Further Read:

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • THen why did the first example fail if you said the bridge method would be added , sorry If I dint understood your answere completly – sumedha Jan 25 '14 at 18:10
  • @sumedha Yes, the bridge method is added. That is not really resulting in failure of your method invocation in `main`. That is completely internal. Problem is, you are passing a `String` type argument to the method (which is exactly a bridge method), which then internally tries to cast that argument to an `Integer` type, which is where is fails. – Rohit Jain Jan 25 '14 at 18:15
  • if second example has replica of bridge method then why dint it fail in the second example please exaplain – sumedha Jan 25 '14 at 18:18
  • @sumedha Which 2nd example are you talking about? If it's the one in your question, then no it's not really a replica of a bridge method. There is no bridge method there. Remember, **the bridge method is created in the subclasses**. – Rohit Jain Jan 25 '14 at 18:21
4

In the second version MyNode.setData doesn't override Node.setData since it has a different signature. Therefore in your second example no dynamic dispatching happens and Node.setData gets called, which can handle the String just fine.

In the first example MyNode.setData does override Node.setData, so it gets called, but it can't handle Strings, it can only handle Integers so you get the ClassCastException.

So if the blog claims this is exactly what happens internally, it is wrong. What they probably mean: It works like this, if MyNode.setData would still override Node.setData in the second example.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348