Tuesday, January 7, 2014

Generics: Bridge Method

I was thinking to improve my algorithmic fundamentals, so I planned to make few notes for myself that will assist me in future (Obviously at the time of Interviews). Following questions and answers have been taken from stackoverflow.com. This post is a sticky notes post for me. Please do not use the answers for reference purpose.

Generics were introduced in Java in 1.5. It provided a tool to handle the casting related problems at the time of run times. With the use of generics ClassCastException has been reduced.

Earlier versions of Java 5 did not impose such generic restrictions, so programmer used to face various casting related problems.

Java 1.4 and prior version were using following formats. There were 2 main issues with these formats.
List list = new ArrayList();//Any class extending Object can be added to list.
list.add("hello");
String s = (String) list.get(0);//Explicit type casting is required.


Java 5 introduced generics which used to type safe the collection objects.
List list = new ArrayList();//Type-safe, it is required to add strings only.
list.add("hello");
String s = list.get(0);   // no cast

What did Java introduce to deal with Generics?
Nothing, While creating the byte code, it removed all generic and replace them with actual classes. So byte code for Java 1.4 version and Java 5 version will remain same. This process called 'ERASUR'.
List list = new ArrayList();
list.add("hello");
String s = (String)list.get(0);

For both the versions following runtime code will execute.

Type Erasur can fail in some conditions, one of such condition is as follows. (Example is taken from Oracles docs)
public class Node {
    private 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);
    }
}

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 Erasur code becomes as following.
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.

Bridge Method?
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, called abridge 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 MyNode classes become:
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(Integer data);
        super.setData(data);
    }
}

After type erasure, the method signatures do not not match. The Node method becomes setData(Object) and the MyNode method becomes setData(Integer). Therefore, the MyNode setData method does not override the Node setData method.
To solve this problem and preserve the polymorphism of generic types after type erasure, a 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);
    }

    // ...
}
As you can see, the bridge method, which has the same method signature as the Node class's setData method after type erasure, delegates to the original setData method.

Note: In this post everything is copied from Java Docs.

No comments:

Post a Comment