bar*_*ary 6 java generics scjp wildcard
我认为我对Java泛型有一些很好的理解.
这段代码没有编译,我知道为什么.
我们只能传递给测试方法类型Animal的类型或它的超类型(如对象列表)
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);
    }        
}
但这里有一个奇怪的部分(至少对我而言).
如果我们通过仅添加<T>将类Test声明为通用,那么它就会编译!并抛出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
我的问题是为什么添加泛型类<T>(它没有在任何地方使用)导致类编译和更改通配符行为?
表达式new Test()是原始类型.Java语言规范定义了原始类型的成员类型,如下所示:
未从其超类或超接口继承的原始类型C的构造函数(第8.8节),实例方法(第8.8节,第9.4节)或非静态字段(第8.3节)M的类型是其类型的擦除在对应于C的泛型声明中.原始类型C的静态成员的类型与对应于C的泛型声明中的类型相同.
擦除List<? super Animal>是List.
这个定义背后的基本原理可能是原始类型旨在作为从非泛型遗留代码使用泛型类型的手段,其中类型参数从不存在.它们没有设计,并且不是最佳的,没有指定类型参数; 这就是通配符类型的用途,即如果您编写的编译器合规性级别大于1.5,则应编写
    Test<?> test = makeTest();
    test.test(animalList);
    test.test(mammalList);
    test.test(dogList);
在再次发现编译错误时欢喜(或诅咒,就此而言).