javagenericsdependency-injectionguiceguice-3

Java Generics : Is any meta information about the generic type preserved at runtime as well?


Background

My understanding of Java generics is it being completely a compile time feature (mainly focusing on type safety checks). The type information for any generic class is lost at runtime (type erasure).

Still, i see many frameworks seem to leverage the type information at runtime as well. For example, google guice Providers. The guice provider can instantiate and provide new instances of it's generic type at runtime.

class Container
{
     @Inject   
     public Container(Provider<Content> contentProvider)
     {
     //This works at Runtime... but How ??? 
     //When type's are not even preserved at runtime, how does the Provider knows it has to instantiate an object of type 'Content' 
     Content content = contentProvider.get();   
     }
}

Question

  1. Is there any information related to generic types which is preserved at runtime as well. ? If yes, what ?. If no, than how does libraries like google guice operate internally (Above example)

  2. Is there more to generics than just compile time safety ? As in, is there any use-case (other than ensuring compile time safety) where one would get advantage using generics ?


Solution

  • Of course the information that a class is generic is supported.

    In other words: when you decompile ArrayList.class you will find hints about the fact that this class allows for one generic type parameter. In other words: class files contain meta information. And using reflection it is possible to inspect this meta information at runtime.

    But when you have another class that uses some List<Integer> object - then you do not find information about that "list uses an Integer" in the compiled class - unless you use some specific patterns, as outlined here for example.

    So the answer is basically: for almost all use cases of practical relevance, "generics" are compile time only.

    Example:

    public class GenericsExample<T> {
      private T member;   
      public T foo(T bar) {
         return member;
      }
    }
    

    Now run: javap -p -c GenericsExample

    Compiled from "GenericsExample.java"
    public class GenericsExample<T> {
      private T member;
    
      public GenericsExample();
        Code:
           0: aload_0       
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return        
    
      public T foo(T);
        Code:
           0: aload_0       
           1: getfield      #2                  // Field member:Ljava/lang/Object;
           4: areturn       
    }
    

    As you can see the decompiler understands that the class uses that generic type T. For more details see here or there.