Wednesday 17 May 2017

Problems with Generics (type erasure)

Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure.

Because of type erasure, type parameters cannot be determined at run-time.
Example, when an ArrayList is examined at runtime, there is no general way to determine whether, before type erasure, it was an ArrayList<Integer> or an ArrayList<Float>.

ArrayList<Integer> intList = new ArrayList<Integer>();
ArrayList<Float> floatList = new ArrayList<Float>();
if (intList.getClass() == floatList.getClass()) { // evaluates to true
    System.out.println("Equal");
}

A generic class cannot extend the Throwable class in any way, directly or indirectly:
public class GenericException<T> extends Exception

The reason why this is not supported is due to type erasure:
try {
    throw new GenericException<Integer>();
} catch(GenericException<Integer> e) {
    System.err.println("Integer");
} catch(GenericException<String> e) {
    System.err.println("String");
} 

Deep dive in Type Erasure
import java.util.HashMap;
import java.util.Map;

public class TypeErasure {
  public static void main(String args[]) {

    Map<String,String> genericMap = new HashMap<>();
   
    Map nonGenericMap = new HashMap();

    genericMap.put("Rajesh", "Developer");
    String role = genericMap.get("Rajesh");
    System.out.println("Role: "+language);
  }
}

We instantiate a generic map using the parameterized type as generic String type. It provides us the type safety and helps avoid the need for explicit casting.

Type erasure is a process to remove these types and map it to raw type in bytecode and it is done during compilation by the Java compiler. Java compiler replaces generic type information from the source and adds casts as needed and delivers the bytecode, generics related information are kept as metadata in the bytecode for debugging, reflection purposes. With respect to general runtime execution, the code will look like plain Java without generics.

Let us compile the above given Java source file and then decompile that generated the binary class file.

C:\Users\awadh\Desktop\interview>javap -c TypeErasure
Compiled from "TypeErasure.java"
public class TypeErasure {
  public TypeErasure();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/HashMap
       3: dup
       4: invokespecial #3                  // Method java/util/HashMap."<init>"
:()V
       7: astore_1
       8: new           #2                  // class java/util/HashMap
      11: dup
      12: invokespecial #3                  // Method java/util/HashMap."<init>"
:()V
      15: astore_2
      16: aload_1
      17: ldc           #4                  // String 1954
      19: ldc           #5                  // String FORTRAN
      21: invokeinterface #6,  3            // InterfaceMethod java/util/Map.put
:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
      26: pop
      27: aload_1
      28: ldc           #4                  // String 1954
      30: invokeinterface #7,  2            // InterfaceMethod java/util/Map.get
:(Ljava/lang/Object;)Ljava/lang/Object;
      35: checkcast     #8                  // class java/lang/String
      38: astore_3
      39: getstatic     #9                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      42: new           #10                 // class java/lang/StringBuilder
      45: dup
      46: invokespecial #11                 // Method java/lang/StringBuilder."<
init>":()V
      49: ldc           #12                 // String Language:
      51: invokevirtual #13                 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      54: aload_3
      55: invokevirtual #13                 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      58: invokevirtual #14                 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      61: invokevirtual #15                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      64: return
}

This is done by the java compiler while compiling java source code to bytecode as part of the process java type erasure. Main reason behind having generics and type erasure is to give the programmer ability to write generic code and type safety. Generics is not forced, the generic type can be directly used as the raw type instead of supplying parameterized type argument. But it is not a good coding practice.

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...