This is the third part of the JVM Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 — Getting object address
Erasure in Java seems pretty easy but sometimes it has unexpected consequences. One of them is erasure of whole class content, not only the generic type. According to JLS 4.6 we have
Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.
Let’s take this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import java.util.*; import java.lang.*; import java.io.*; class Ideone { public static List<? extends Object> produce(){ return null; // Whatever } public static void main (String[] args) throws java.lang.Exception { } public static void a(NoGeneric noGeneric){ noGeneric.call(produce()); } public static <T> void b(Generic<T> generic){ generic.call(produce()); } public static <T, U extends Generic<T>> void d(U generic){ generic.call(produce()); } public static <T extends Generic> void c(T raw){ raw.call(produce()); } } class NoGeneric{ public void call(List<Object> objects){ } } class Generic<T> { public void call(List<Object> objects){} } |
Compiler signals this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Main.java:16: error: incompatible types: List<CAP#1> cannot be converted to List<Object> noGeneric.call(produce()); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? extends Object Main.java:20: error: incompatible types: List<CAP#1> cannot be converted to List<Object> generic.call(produce()); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? extends Object Main.java:24: error: incompatible types: List<CAP#1> cannot be converted to List<Object> generic.call(produce()); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? extends Object Note: Main.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 3 errors |
Line 16 doesn’t work because we try to pass list of ? extends Object
to list of Object
. Java doesn’t allow this for generic types (it would work for arrays, though).
Line 20 doesn’t work because generic of T
is not a raw type so we do the same as in line 16.
Line 24 doesn’t work because of the same reasoning.
However, line 28 works. It is because T extends Generic
uses a raw type. According to JLS we remove generic parameters not only related to T
in Generic
but to other things as well. So method call(List< Object > objects)
becomes call(List objects)
.