Reputation: 37034
I investigate generic behaviour
I noticed that:
public class Hohol1 {
public class My<T> {
public <E> void test(Collection<E> es) { System.out.println("Collection<E>");
}
public void test(List<Integer> integerList) {
System.out.println("List<Integer>");
for (Integer integer : integerList) {
System.out.println(integer);
}
}
}
public static void main(String[] args) {
My my1 = new Hohol1().new My();
my1.test(new ArrayList<String>() { {add("1");} });
}
}
code above returns
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at GenericsTest.Hohol1$My.test(Hohol1.java:22)
at GenericsTest.Hohol1.main(Hohol1.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
But if I make My
non-generic so
public class Hohol1 {
public class My/*deleted generic type T*/ {
....
}
}
this code returns Collection<E>
It is surpised behaviour for me and I don't understand why it created so.
What Do you think about it?
P.S. I use double brace initialization for laconic
Upvotes: 2
Views: 137
Reputation: 178263
First, when you are using the raw form of the inner class My
, everything in the class, including unrelated specified type parameters such as the method parameter List<Integer> integerList
act as if their own type erasure were in place, e.g. List integerList
.
However, you pass in an anonymous subclass of ArrayList
(effectively just an ArrayList
) of String
s. The most specific method that applies is test(List<Integer> integerList)
, because it's viewed as List
, which your ArrayList
certainly is. Everything still runs until the enhanced for
loop that attempts to print Integer
s, but it can't convert a String
such as "1"
to an Integer
, so you get the ClassCastException
.
But if you make My
non-generic, then the type of the parameter integerList
is not erased. The compiler then can see that List<Integer>
doesn't match, but Collection<E>
does match, so test(Collection<E> es)
matches and "Collection<E>"
is printed.
The reason is specified in the JLS, Section 4.8:
More precisely, a raw type is defined to be one of:
- The reference type that is formed by taking the name of a generic type declaration without an accompanying type argument list.
...
and
The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.
The reasoning is also given:
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.
Upvotes: 3