泛型常用特点,List能否转为List
简单说不可以,String 是 Object 的子类型,但是 List<String>
不是 List<Object>
的子类型,让我们为什么直观上会觉得 List<String>
和 List<Object>
存在父子关系呢?那是因为 java 编译器认为 String[] 是 Object[] 的子类型。但是这个设计有些问题,看如下代码
Object[] myArray = new String[]{"a", "b", "c"};
myArray[1] = 1;
System.out.println(myArray[1]);
上面的代码能通过编译,但是会抛一个运行时异常,ArrayStoreException。虽然 Java 编译器认为 String[] 是 Object[] 的子类型,但是事实上并不是,它不符合里氏替换原则。所以在后来 JDK 引入范型的时候,就没有再让 List<String>
是 List<Object>
的子类型。但为了兼容已有代码,数组已经改不过来了。
里氏替换原则Liskov substitution principle
如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都换成 o2 时,程序P的行为没有变化,那么类型 T2 是类型 T1 的子类型。
通俗定义
所有引用基类的地方必须能透明地使用其子类的对象。
更通俗定义
子类可以扩展父类的功能,但不能改变父类原有的功能。
那问题又来了,如果 List<String>
和 List<Object>
没有父子关系,那我们开发中碰到需要使用超类容器的场景该怎么办?
举个例子 Apple 是 Fruit 的子类,Fruit 是 Food 的之类。有一个 Plate 范型类,把 Apple,Fruit 和 Food 作为参数类型。
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
//不能存入任何元素
p.set(new Fruit()); //Error
p.set(new Apple()); //Error
//读取出来的东西只能存放在Fruit或它的基类里。
Fruit newFruit1=p.get();
Object newFruit2=p.get();
Apple newFruit3=p.get(); //Error
对于只读场景,Plate<? extends Frunt>
是 Plate<Apple>
的超类
Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());
//存入元素正常
p.set(new Fruit());
p.set(new Apple());
//读取出来的东西只能存放在Object类里。
Apple newFruit3=p.get(); //Error
Fruit newFruit1=p.get(); //Error
Object newFruit2=p.get();
对于只写场景,Plate<? super Frunt>
是 Plate< Fruit>
的超类
这就引入了一个新的类型感念:变形 variance
类型 | 说明 | 实例 |
---|---|---|
协变covariant | 组合类型保持原类型的父子关系 | Cat[] Animal[] 以及 List<Animal> List<? extends Animal>
|
逆变contravariant | 组合类型反转原类型的父子关系 |
List<Cat> List<? super Cat>
|
不变invariant | 组合类型忽略原类型的父子关系 |
List<Cat> List<Animal>
|
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。