文档

Java™ 教程
隐藏目录
泛型、继承和子类型
路径: 学习 Java 语言
课程: 泛型(已更新)

泛型、继承和子类型

正如你已经知道的,可以将一个类型的对象分配给另一个类型的对象,前提是这些类型是兼容的。例如,你可以将一个Integer分配给一个Object,因为ObjectInteger的超类型之一:

Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;   // OK

在面向对象的术语中,这被称为“是一个”关系。由于IntegerObject的一种类型,所以允许进行赋值。但是Integer也是Number的一种类型,所以下面的代码也是有效的:

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>的子类型的图表
Box<Integer>不是Box<Number>的子类型,尽管IntegerNumber的子类型。

注意: 给定两个具体类型AB(例如,NumberInteger),MyClass<A>MyClass<B>没有关系,无论AB是否相关。MyClass<A>MyClass<B>的共同父类是Object

有关如何在类型参数相关时创建两个泛型类之间类似子类型的关系的信息,请参阅通配符和子类型

泛型类和子类型

您可以通过扩展或实现泛型类或接口来创建子类型。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由extendsimplements子句确定。

Collections类为例,ArrayList<E>实现List<E>List<E>扩展Collection<E>。所以ArrayList<String>List<String>的子类型,List<String>Collection<String>的子类型。只要类型参数不变,类型之间的子类型关系就会保持。

示例集合层次结构的图表:ArrayList<String>是List<String>的子类型,List<String>是Collection<String>的子类型。
一个示例的Collections层次结构

现在假设我们想要定义自己的列表接口PayloadList,它将泛型类型P的可选值与每个元素相关联。它的声明可能如下所示:

interface PayloadList<E,P> extends List<E> {
  void setPayload(int index, P val);
  ...
}

PayloadList的以下参数化类型是List<String>的子类型:

示例PayLoadList层次结构的图表:PayloadList<String, String>是List<String>的子类型,List<String>是Collection<String>的子类型。在PayloadList<String,String>的同一级别上是PayloadList<String, Integer>和PayloadList<String, Exceptions>。
一个示例的PayloadList层次结构

上一页:泛型方法和有界类型参数
下一页:类型推断