这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
泛型、继承和子类型
正如你所知的,只要类型是兼容的,就可以将一种类型的对象分配给另一种类型的对象。例如,可以将一个Integer赋值给一个Object,因为Object是Integer的超类型之一:
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // OK
在面向对象的术语中,这称为“is a”关系。由于Integer是一种Object,所以允许赋值。但是Integer也是一种数字,所以下面的代码也是有效的:
public void someMethod(Number n) { /* ... */ }
someMethod(new Integer(10)); // OK
someMethod(new Double(10.1)); // OK
泛型也是如此。你可以执行泛型类型调用,将Number作为其类型参数传递,如果该参数与Number兼容,则允许对add的任何后续调用:
Box<Number> box = new Box<Number>();
box.add(new Integer(10)); // OK
box.add(new Double(10.1)); // OK
考虑下面的方法:
public void boxTest(Box<Number> n) { /* ... */ }
它接受什么类型的参数?通过查看它的签名,可以看到它接受一个类型为Box<Number> 的参数。但这意味着什么呢?是否可以像预期的那样传递Box<Integer> 或 Box<Double> ?答案是否定的,因为 Box<Integer> 和 Box<Double> 不是 Box<Number> 的子类型。
在使用泛型编程时,这是一个常见的误解,但它是一个需要学习的重要概念。
Box<Integer>不是Box<Number>的子类型,即使Integer是Number的子类型。
注意: 给定两种具体类型A和B(例如 Number 和Integer), MyClass<A> 与 MyClass<B> 没有关系,不管A和B是否相关。MyClass<A> 和 MyClass<B> 的共同父类是Object。
泛型类和子类型
可通过扩展或实现泛型类或接口来对其进行子类化。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由extends和implements子句确定。
以集合类为例,ArrayList<E>实现了List<E>, List<E>扩展了Collection<E>。ArrayList<String>是List<String>的子类型,List<String>是Collection<String>的子类型。只要不改变类型参数,类型之间的子类型关系就会保持不变。
示例集合层次结构
现在,假设我们要定义自己的列表接口PayloadList,它将泛型类型P的可选值与每个元素关联起来。它的声明可能是这样的:
interface PayloadList<E,P> extends List<E> {
void setPayload(int index, P val);
...
}
以下参数化的PayloadList是List<String>的子类型:
- PayloadList<String,String>
- PayloadList<String,Integer>
- PayloadList<String,Exception>