Yay! No overhead! Lets see how this erasure thingy works in practice. javap will let us peek at the generated code and see what actually is going on. We have:
>java -version java version "1.8.0_72"
Case 1
public <T> void process(T t) {}
We don't really care about the method body, just the generated method signatures, javap -s produces:
public <T> void process(T); descriptor: (Ljava/lang/Object;)VIn this basic case the parameter T is replaced by Object.
Case 2
It is valid to use a "bounded type parameter" that is, to have an upper bound on the type T can assume. This is expressed by using extends which is sort of combined extends/implements like so:
public <T extends Serializable> void process1(T t) {}Looking at the compiled code we see:
public <T extends java.io.Serializable> void process1(T); descriptor: (Ljava/io/Serializable;)VLooks like the compiler is replacing T with the most specific super class/interface. It should be all good, after all, Java is single inheritance. Except that off course we can implement as many interfaces as we like, which is supported by the generics implementation, and so that we have
Case 3
public <T extends Comparable & EventListener > void process2(T t) {}Now the compiler has to choose, what would that be...
public <T extends java.lang.Comparable & java.util.EventListener> void process2(T); descriptor: (Ljava/lang/Comparable;)VSo Comparable won. Is this because of the ordering?
Case 4
public <T extends EventListener & Comparable> void process3(T t) {}
and indeed, this time EventListener comes out on top:
public <T extends java.util.EventListener & java.lang.Comparable > void process3(T); descriptor: (Ljava/util/EventListener;)VHere is the complete java code:
public class Erasure { public <T> void process(T t) {} public <T extends Serializable> void process1(T t) {} public <T extends Comparable & EventListener> void process2(T t) {} public <T extends EventListener & Comparable> void process3(T t) {} }
Thanks Vlad, fixed the typo.
ReplyDelete