Java泛型简明解释

Java泛型由来的动机

理解Java泛型最简单的方法是把它看成一种便捷语法,能节省你某些Java类型转换(casting)上的操作:

List<Apple> apples=...
Apple apple=apples.get(1);

如上的代码,就不用程序员手动做类型判断了,因为泛型,在编译阶段编译器就对类型进行了检查

泛型的构成

由泛型的构成引出了一个类型变量的概念。根据Java语言规范,类型变量是一种没有限制的标志符,产生于以下几种情况:

  • 泛型类声明

  • 泛型接口声明

  • 泛型方法声明

  • 泛型构造器(constructor)声明


泛型类和接口

public interface List<T> extends Collections<T>{

}

泛型方法和构造器(Constructor)

public static <T> getFirstItem(List<T> list){
}

泛型类型的子类型

假设有这么三种水果:

FujiApple,

Apple,

Fruit

FujiApple是Apple的子类,Apple是Fruit的子类,那么

List<FujiApple>,

List<Apple>,

List<Fruit>,

三者之间是什么关系呢?

List<Apple> apples=new ArrayList<Apple>();
List<Fruit> fruits=apples;

上面这段代码正确吗?答案是编译出错。

为什么呢?看下面这段代码

List<Apple> apples=new ArrayList<Apple>();
List<Fruit> fruits=apples;
fruits.add(new Strawberry());//Strawberry是草莓,是水果的子类型

如果Apple是Fruit的子类型,就认为List<Apple>是List<Fruit>的子类型,那么List<Fruit>自然就可以add草莓了。

但是这就违背了泛型是用来确定类型的这么一个公理。这样List<Fruit>中就用了多种类型了,那么拿出来的时候还是要手动

判断类型了。

所以List<Apple>跟List<Fruit>没有关系。

通配符

向上造型一个泛型对象的引用

List<Apple> apples=...
List<? extends Fruit> fruits=apples

如上代码,如果你想要两个泛型对象具有继承关系,可以使用通配符。

“? extends”是泛型类型的子类型相关性成为现实:Apple是Fruit的子类型,List<Apple> 是 List<? extends Fruit> 的子类型。

但是你从此不能往fruits里加入任何数据了,当然取出Fruit是可以的。

向下造型一个泛型对象的引用

List<Fruit> fruits=...
List<? super Apple> apples=fruits;

我们看到fruits指向的是一个装有Apple的某种超类(supertype)的List。同样的,我们不知道究竟是什么超类,但我们知道Apple和任何Apple的子类都跟它的类型兼容。既然这个未知的类型即是Apple,也是GreenApple的超类,我们就可以写入:

apples.add(new GreenApple);

apples.add(new Apple());

如果你试图往apples里加入任何apple的超类,编译器会警告你。

apples.add(new Fruit());

apples.add(new Object());

但是你需要取出对象的时候,取出的只能是Object对象。

存取原则和PECS法则

总结 ? extends 和 the ? super 通配符的特征,我们可以得出以下结论:

  • 如果你想从一个数据类型里获取数据,使用 ? extends 通配符

  • 如果你想把对象写入一个数据结构里,使用 ? super 通配符

  • 如果你既想存,又想取,那就别用通配符。

这PECS是指”Producer Extends, Consumer Super”

参考书籍:

  • The Java Tutorial

  • Java Generics and Collections, by Maurice Naftalin and Philip Wadler

  • Effective Java中文版(第2版), by Joshua Bloch.


你可能感兴趣的:(java,泛型)