Person 是个中间的类,上面有父类 Bean,下面有子类 Student。
对于传入数据
当用 extends 定义了上界时,就不是再传入 Person 的父类了,只能传入自身或子类,而且(如果是定义容器,如 List<? extends Fruit>)只能是只读形式,不能添加。这是因为父类 Bean 中可能没有 Person 中定义的方法,如果可以传入并调用的话,会出错。只读是因为如果可以添加的话,那么在传递的过程中,就可以把原先传入的子类改为其它子类,那么类型就会错乱,比如原先的子类A中只有 read 方法,在方法中替换成其它只有 write 方法的子类B,当列表返回再调用 read 方法时就会导致代码出错,所以系统才会把定了上界的类型设置为只读。
(当使用了泛型的通配符之后,确实可以实现将ArrayList进行向上转型了,实现了泛型的协变,但是却再也不能往容器中放任何东西了,连Apple本身都被禁止了。因为,在定义了fruitList之后,编译器只知道容器中的类型是Fruit或者它的子类,但是具体什么类型却不知道,编译器不知道能不能比配上就都不允许比配了。类比数组,在编译器的时候数组允许向数组中放Fruit和Orange等非法类型,但是运行时还是会报错,泛型是将这种检查移到了编译期,协变的过程中丢失了类型信息。)
当用 super 定义了下界时,就不是再传入 Person 的子类了,只能传入自身或父类,而且(如果是定义容器,如 List<? super Fruit>)可以添加(自身及子类,确定是子类)。这是为了添加需要,如果可以传入子类的话,比如子类A的列表,这时再往里面加入子类B的话,就会出错,但如果是父类的话,就没有问题。
(可以看到使用super就可以实现泛型的逆变,使用super的时候指出了泛型的下界是Apple,可以接受Apple的父类型,既然是Apple的父类型,编辑器就知道了向其中添加Apple或者Apple的子类是安全的了,所以,此时可以向容器中进行存,但是取的时候编辑器只知道是Apple的父类型,具体什么类型还是不知道,所以只有取值会出现编译错误,除非是取Object类型。)
对于取数据
当用 extends 定义了上界时,取数据是安全的,因为父类有了上界,用父类的类型即可。
当用 super 定义了下界时,取数据是不安全的,因为不知道传进去的具体类型是什么,取出来了也不确定对象中有没有想调用的方法,所以不安全。
通配符上限,取出安全,传入不可靠。
通配符下限,传入安全,取出不可靠。
- 要从泛型类取数据时,用extends;
- 要往泛型类写数据时,用super;
- 既要取又要写,就不用通配符(即extends与super都不用)。
Java泛型中的PECS原则