Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java type erasure - why can I see the type when I look at the bytecode?

I am trying to understand why writing both methods in a class is not allowed

 public bool plus(List<String>) {return true;}
 public bool plus(List<Integer>) {return true;}

I try to figure how it is related to type erasure but when I decompile the following code

public class Test<T> {

   boolean plus2(List<T> ss) {return false;}
   boolean plus(List<String> ss) {return false;}
   boolean plus(Set<Integer> ss) {return false;}
}

I get the same when I decompile it with Java decompiler (jd)

Even when I print the byte code I can clearly see the types.
(Looking at SO answer that declares 'but rest assure the types are erased in the bytecode' )

    Compiled from "Test.java"
    public class com.example.Test<T> {
    public com.example.Test();
            Code:
            0: aload_0
            1: invokespecial #1                  // Method java/lang/Object."<init>":()V
            4: return

            boolean plus2(java.util.List<T>);
            Code:
            0: iconst_0
            1: ireturn

            boolean plus(java.util.List<java.lang.String>);
            Code:
            0: iconst_0
            1: ireturn

            boolean plus(java.util.Set<java.lang.Integer>);
            Code:
            0: iconst_0
            1: ireturn
            }
like image 538
Bick Avatar asked Mar 17 '16 10:03

Bick


1 Answers

Your compiler needs to be able to check the generic type information based on information in the byte code.

Java 5.0+ records the generic information in byte code, but it doesn't record it in the object instance.

e.g. there is no way to get the generic type of this List

 // list.getClass() is just ArrayList which doesn't record the generic type.
List list = new ArrayList<String>();

however this does

// list.getClass() is not ArrayList
// list is an instance of a class which extends ArrayList<String>
List list = new ArrayList<String>() { };

ParameterizedType t = (ParameterizedType) list.getClass().getGenericSuperclass();
assert t.getRawType() == ArrayList.class;
assert t.getActualTypeArguments()[0] == String.class;

This is because the byte code of the sub-class of ArrayList records the generic used in it's parent.

Note: this also works for fields, constructor/method arguments as return types and the actual type is recorded in the byte code.

However, none of these mean the generic type of an instance is available (though the generic type of a parent class/interface could be available if recorded in byte code.

like image 90
Peter Lawrey Avatar answered Oct 19 '22 21:10

Peter Lawrey