直观理解Java泛型的子类型

tech2022-08-21  114

Java泛型的子类型问题

存在对象及容器两个问题

1,对象类型的子类型,比如:苹果是不是水果的子类型?

2,容器类型的子类型,比如:一盒苹果是不是一盒水果的子类型?

 

继承与指针

子类型问题的根源在于继承

子类继承基类后,子类除了拥有基类的一切外,子类还有自己仅有的部分属性

举例:苹果继承了水果,苹果拥有水果的所有属性,但还有自己的属性,比如苹果的酸甜

 

子类型问题具体表现在指针与对象的匹配上

1,一个自声明的指针:我是一个指向苹果的指针

2,一个被指向的对象,比如一个水果或一个苹果

 

指针的称职

1,一个自声明为水果的指针指向一个苹果,该指针称职,从该指针你一定可以取回一个水果

2,一个自声明为苹果的指针指向一个水果,该指针不称职,从该指针你不一定能取回一个苹果,你只能取回一个水果,甚至其可能是一根香蕉

总结:一个苹果可以赋给一个自声明为水果的指针,但反之不行,会导致该指针不称职

 

容器

以List容器为例

List<Apple>表示一盒苹果,也即容器中存在若干指针,这些指针自声明指向苹果

List<Fruit>表示一盒水果,也即容器中存在若干指针,这些指针自声明指向水果

 

容器可以的操作

写入:

List<Fruit>可以写入水果对象,也即容器中的指针可以指向水果对象

List<Fruit>也可以写入苹果对象,因为容器中自声明指向水果的指针也可以指向苹果

List<Apple>不可以写入水果对象,因为容器中指针自声明为苹果,如果挂入水果则少了苹果的特性

读出:

List<Apple>可以读出苹果对象,也即从容器中指针可以读出苹果对象

List<Apple>也可以读出水果对象,毕竟苹果对象中包含一个水果对象

List<Fruit>不可以读出苹果对象,因为容器中指针指向水果,该水果可能缺乏苹果的属性

 

容器子类型

List<Fruit>与List<Apple>的子类型关系

 

从容器写入考察

List<Fruit>可以写入Fruit及Apple,List<Apple>可以写入Apple

可见List<Fruit>比List<Apple>写入更多

从容器读出考察

List<Fruit>可以读出Fruit,List<Apple>可以读出Apple及Fruit

可见List<Apple>比List<Fruit>读出更多

 

可见从容器特性上没有谁能包含谁,也即相互之间都没有子类型关系

 

通配符容器

List<? Super Apple>:

这个容器里面指针槽的指针是<? Super Apple>,也即苹果或水果,或更上层的“可食用植物”

根据指针,这个容器总可以添加苹果,红苹果,烟台红苹果

根据指针,除Object之外,不敢保证能完整地读出什么,甚至不能保证读出“可食用植物”,因为指针类型可能是“植物”

相对于List<Apple>限制了读

 

List<? Extends Apple>:

这个容器里面指针槽的指针是<? Extends Apple>,也即苹果或红苹果

根据指针,这个容器不能写入任何对象,因为指针类型可能是“烟台红苹果”,除非我们写入“烟台市xxx果园的红苹果”

根据指针,这个容器总可以读出苹果或水果,或更上层的“可食用植物”

相对于List<Apple>限制了写

 

通配符容器子类型

List<? extends Apple> 是 List<? extends Fruit> 的子类型

因为前者不仅可以读出Fruit,还可以读出Apple,在写方面是两者都不可写

由于 Apple 本身是 Fruit 的子类型,而容器也是如此对应关系,所以这里是 型变

 

型变:如果 aa 是 a 的子类型,同时 Holder<aa> 也是 Holder<a>的子类型

 

List<? super Fruit> 是 List<? super Apple> 的子类型

因为前者不仅可以写入Apple,还可以写入 Fruit

由于 Apple 本身是 Fruit 的子类型,而容器的子类型关系相反,所以这里是 逆变

 

逆变:如果 aa 是 a 的子类型,同时 Holder<a> 是 Holder<aa>的子类型

 

通配符容器之间存在子类型关系,其根源在于通配符限制了容器的读或写

最新回复(0)