Java wildcards behave strangely when classes are generic
I think I have some good understanding of Java generics
This code is not compiled. I know why
We can only pass the type of test method type animal or its supertype (such as object list)
package scjp.examples.generics.wildcards; import java.util.ArrayList; import java.util.List; class Animal {} class Mammal extends Animal {} class Dog extends Mammal {} public class Test { public void test(List<? super Animal> col) { col.add(new Animal()); col.add(new Mammal()); col.add(new Dog()); } public static void main(String[] args) { List<Animal> animalList = new ArrayList<Animal>(); List<Mammal> mammalList = new ArrayList<Mammal>(); List<Dog> dogList = new ArrayList<Dog>(); new test().test(animalList); new test().test(mammalList); // Error: The method test(List<? super Animal>) in the type Test is not applicable for the arguments (List<Mammal>) new test().test(dogList); // Error: The method test(List<? super Animal>) in the type Test is not applicable for the arguments (List<Dog>) Dog dog = dogList.get(0); } }
But there is a strange part here (at least for me)
If we declare that the class test is generic by adding only < T >, it will compile! And throw Java lang.ClassCastException:
public class Test<T> { ... }
,
Exception in thread "main" java.lang.ClassCastException: scjp.examples.generics.wildcards.Animal cannot be cast to scjp.examples.generics.wildcards.Dog
My question is why adding a generic class type < T > (which is not used anywhere) causes class compilation and changes wildcard behavior?
Solution
The expression new test() is of primitive type The member types of the original type of Java language specification definitions are as follows:
Deletion of list is a list
The rationale behind this definition may be that primitive types are intended as a means of using generic types from non generic legacy code, where type parameters never exist They are not designed, are not optimal, and do not specify type parameters; This is the purpose of wildcard type, that is, if you write a compiler compliance level greater than 1.5, you should write
Test<?> test = maketest(); test.test(animalList); test.test(mammalList); test.test(dogList);
Rejoice (or curse, for that matter) when a compilation error is found again