How generics works internally in java

Generics was added in java as part of JDK 5. It is one of the key area from where questions being asked at senior java developer level and expected to have solid understanding of generics fundamentals and its internal workings. The main agenda of this article to understand internal workings of Generics, click here for detailed discussion of generics fundamental.
Lets understand fundamental of generics with an example, followed by we will see the internal  of generics. What is generics in java - Generics provides a way for you to communicate the type of a collection to the compiler, so that compiler will verify whether you have used the collection consistently or not and provide tighter type checks at compile time to support generic programming. In other words, in order to avoid  run time failure of java program, generics provides a mechanism which force compiler to check for correctness of type (allowed object for placeholder). Lets see this sample code :
class GenericsSample<T extends Number> {
 private T data;
 private T[] intArray;
 public GenericsSample(T data){
  this.data = data;
 }
 
 // methods for finding sum of all numbers of intArray.
}
In the above sample code, class name has been appended with <T extends Number> i.e: GenericsSample <T extends Number>. By doing so, we instruct compiler that T is a placeholder and only acceptable values for this placeholder is of type Number(Integer,Float, Double,etc.). Please note, primitives are not allowed in generics, that's why Integer not int.
Why we need generics ? - generics gives developer an opportunity to fix java program problems at compile time rather than dragging it to run-time failure, as we know run-time failure is a nightmare.
Now come back to main agenda of this article, to understand the internal working of generics.Generic code had to be compatible with preexisting non-generic code(java 4 and before)., so how does backward compitablity has been maintained with generics introduction. The way Java implements generics while satisfying the constraint(to avoid breaking older code) is through the use of erasure. To implement generics, the Java compiler applies type erasure.
What is this erasure and how does it important for generics ?
Erasure is that entity which understand raw java code(embedded with generics syntax) and convert it into plain ordinary classes, interfaces, and methods at compile time,so that generics incur no run-time overhead.
How erasure makes generics embedded code compatible with older java code ?
1. First, when Java code is compiled, all generic type information is removed (erased) and type parameters is replaced with their bound type, which is Object if no explicit bound is specified.The produced byte-code, therefore, contains only ordinary classes, interfaces, and methods.
2. Insert type casts,  if necessary to preserve type safety.
3. Generate bridge methods to preserve polymorphism in extended generic types.
Lets consider the following two scenario to understand how a java class with generics syntax transformed independent of generics.
Case 1: Type parameter is unbounded-  Column 1 represents java class with generics syntax embedded and column 2 is compiled version after applying erasure:

Since the type parameter T is unbounded(not super class or subclass specified), the Java compiler replaces all placeholder T with Object(parent of all class).
Case 2: Type parameter is bounded- Column 1 represents java class with generics syntax embedded and column 2 is compiled version after applying erasure:
Here the type parameter T is bounded(String super class specified), the Java compiler replaces all placeholder T with java.lang.String.
Now we will see generics in class hierarchy(involved method overriding) and discuss the importance of Bridge method in it.Consider following example with Node as parent class and MyNode as child class extending parent class Node.
//parent class
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;
    }
}
//child class
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { 
    super(data); 
 }
    public void setData(Integer data) { // overrididng method 
        System.out.println("MyNode.setData");
        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).
public void setData(T data) {  ....  } transformed  to 
                   public void setData(Object data) { ...  }
   and 
 public void setData(Integer data) { ... } tranformed to 
                      public void setData(Integer data) {...}
Therefore, the MyNode setData method does not override the Node setData method and  doesn't preserve the polymorphism of generic types. Here Bridge method comes for rescue, Java compiler generate bridge method in child class and ensure that subtyping works as expected.See below diagram, we have bridge method generated in child class  "setData(Object data) { }" overriding parent class method.
So, now bridge method delegates call to original setData(Integer) and maintains polymorphoism.
This is all about generics internal. Please refer this for understanding restriction in generics.

                                                 ==========End of article=========
Happy learning
Nikhil

2 Comments

Previous Post Next Post