集合泛型的不变性,而数组具有协变性,注意赋值容易导致的出错
泛型的实际应用情况和通配符的使用,关于大范围和小范围互相赋值的时候JVM编译报错的问题:
问题描述:继承关系
class A {} class B extends A {} class C extends A {} class D extends B {}
下面给出了几种说法:
The type List<A>is assignable to List. The type List<B>is assignable to List<A>. The type List<Object>is assignable to List<?>. The type List<D>is assignable to List<?extends B>. The type List<?extends A>is assignable to List<A>. The type List<Object>is assignable to any List reference. The type List<?extends B>is assignable to List<?extends A>.
(1)首先谈泛型只是在编译期保证对象类型相同的技术,比如在指定的类中声明了一个泛型,那么表示在此类中某一个属性的类应该是声明的这种类型的,而真正在代码运行的时候JVM会查出来泛型的存在。
(2)Java数组具有协变性、集合不具备协变特性
function(Person person);
对于上面这个函数,如果我传入的是一个Student student对象的话,编译期是不会报错的,这是多态的特性。
function(Person[] person);
而对于上面的这个函数如果我传入的是一个Student[] student数组进去的话,编译期也不会报错,这是数组的协变性。
public class TestConvert { public static void main(String[] args) { Student[] students = new Student[10]; System.out.println(TestConvert.function(students)); } public static int function(Person[] persons) { return 1; } }
(3)但是如果我的函数是这样的:我想要传进去一个List<Student> students的话,编译器就会报错,这就是集合的泛型不变性,这点与数组的协变性不同。
function(List<Person> persons);
我们可以将泛型改成这样 function (List <? extends Person> ),这样之后,当我们再传入一个List <Student> students集合进去,编译器就就不会报错了。也就是说可以传入包含Person的子类的List了。也就是引进了通配符的使用。
注:<? extends A> 代表小于等于A的范围,<? super A>代表大于等于A的范围,<?>代表全部范围。
换一句话说List<A>和List<B>的集合对象不能直接来赋值的,而只有是一个范围的情况才可以被赋值进去,比如List<? extends A>这个就代表的是一个大的范围,可以将其子类的集合对象List<B>放进去。