Java 泛型解惑之和
1 为什么要用通配符和边界?
使用泛型的过程中,经常出现一种很别扭的情况
比如我们有Fruit类,和它的派生类Apple
class Fruit {}
class Apple extends Fruit {}
然后有一个最简单的容器:Plate类
盘子里可以放一个泛型的“东西”
我们可以对这个东西做最简单的“放”和“取”的动作:*set( )和get( )*方法
class Plate<T>{
private T item;
public Plate(T t){item=t;}
public void set(T t){item=t;}
public T get(){return item;}
}
现定义一个“水果盘”,逻辑上水果盘当然可以装苹果
Plate<Fruit> p = new Plate<Apple>(new Apple());
但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。
error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>
实际上,编译器认定的逻辑是这样的:
- 苹果 IS-A 水果
- 装苹果的盘子 NOT-IS-A 装水果的盘子
所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系
所以我们不可以把Plate的引用传递给Plate
为了让泛型用起来更舒服,Sun的大师们就想出了<? extends T>和<? super T>的办法,来让”水果盘子“和”苹果盘子“之间发生正当关系
2 上界
下面就是上界通配符(Upper Bounds Wildcards)
Plate<? extends Fruit>
一个能放水果以及一切是水果派生类的盘子
再直白点就是:啥水果都能放的盘子
这和我们人类的逻辑就比较接近了
Plate<? extends Fruit>和Plate最大的区别就是:Plate<? extends Fruit>是Plate及Plate的基类
直接的好处就是,我们可以用“苹果盘”给“水果盘”赋值了
Plate<? extends Fruit> p = new Plate<Apple>(new Apple());
再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛肉,苹果还有两种青苹果和红苹果
//Lev 1
class Food{}
//Lev 2
class Fruit extends Food{}
class Meat extends Food{}
//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}
//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}
在这个体系中,上界通配符Plate<? extends Fruit>
覆盖下图中蓝色的区域
相关文章