数组协变性 ,泛型不可协变性
数组的协变性:如果类A是类B的子类,那么A[]是B[]的子类
泛型(<>)不可协变性:如果类A是类B的子类,List<A>
和List<B>
毛线关系都木有
举个例子
Object[] obj = new String[]{};
这个写法是完全OK的,编译通过,而且即便在obj中存放了非string对象,也会在运行时才报异常
这就是数组的协变性,Object[] 是 String[] 的父类
ArrayList<Object> obj = new ArrayList<Integer>();
这个写法完全是错误的,连编译都不通过,因为泛型的非协同性,List<Object>
和 LIst<Integer>
是完全没有关系的两个类 ,那有怎么能new
那么为什么呢
数组为什么是协变性的
数组的协变性来源于数组的一个优势
数组记得它内部元素的具体类型,并且会在运行时做类型检查
也正是因为数组会在运行时做类型检查,在String[]插入一个Integer会报错,这也保证虽然数组是协变性的,依然能保证安全
泛型为什么是不协变性的
那么泛型为什么不是协变性的呢,为什么不能为泛型添加运行做类型检查的机制呢,这是因为泛型的一个特性——擦除性
泛型的擦除性
擦除性:去除泛型类型中所有的类型参数信息,只映射为一份字节码,具体的参数信息只能在静态编译期保留。(这个擦除性在泛型中详细解释,包括如何擦除的等等)
所以,作为只能在编译器保留类型的泛型,只能选择在编译期进行类型检查,因为一旦运行起来这个类型就被擦除掉了呀呀呀,这也就是为什么ArrayList<Object> obj = new ArrayList<Integer>();
编译不会通过的原因。
那么如何能让泛型拥有类似协变性的功能呢
那么再次回到通配符的问题了
像这种实例化对象时使用通配符? ,我好像在哪篇里面写了,在这里再强化一下好了
<? extends xx>
<? super xx>
这样
ArrayList<? extends Object> list = new ArrayList<Integer>;
就OK了,我记得原来说<? extends xx>
表示的是xx的一个子类,再并不在意是什么类型,当时书上还写是为了向上转型,这样就串起来了
PS : 是一种类型嗷嗷嗷啊,?表示的也是xx中确定的一种类型
泛型不可协变性的混淆点
父类A 子类B
泛型的不可协变性是说List<A>
和List<B>
没有关系
可没有改变类A 和 类B之间的继承关系,类A的对象和类B的对象依然是可以转型的
但是这里注意一下,向上转型的主客问题,向上转型是子类B转成父类A,存放在父类A的List中
即父类的List中存放子类对象
ArrayList<Object> list = new ArrayList<Object>();
Integer i = new Integer(2);
list.add(i);
这个代码OKOK的