众所周知,只要类型兼容,就可以将一种类型的对象分配给另一种类型的对象。例如,你可以指定一个Integer给一个Object,因为Object是Integer的一个超类型(supertypes)

  1. Object someObject = new Object();
  2. Integer someInteger = new Integer(10);
  3. someObject = someInteger; // OK

在面向对象的术语中,这称为“是(is a)”关系。由于Integer 是_Object 的_一种,因此允许分配。但是Integer也是Number的一种,因此以下代码也有效:

  1. public void someMethod(Number n) { /* ... */ }
  2. someMethod(new Integer(10)); // OK
  3. someMethod(new Double(10.1)); // OK

泛型也是如此。您可以执行泛型类型调用,将Number用作其类型实参,如果其他实参和Number兼容,则随后可以进行add方法的任意调用:

  1. Box<Number> box = new Box<Number>();
  2. box.add(new Integer(10)); // OK
  3. box.add(new Double(10.1)); // OK

现在考虑以下方法:

  1. public void boxTest(Box<Number> n) { /* ... */ }

它接受哪种类型的实参?通过查看其签名,您可以看到它接受一个类型为Box 的单个实参。但是,这是什么意思?如您所料,您是否允许传递Box 或Box ?答案是“否”,因为Box 和Box 不是Box 的子类型。
在使用泛型进行编程时,这是一个常见的误解,但它是一个重要的概念。
泛型,继承和子类型 - 图1
Box 不是Box 的子类型,即使Integer是Number的子类型。


注意: 给定两个具体类型A和B(例如Number和Integer),无论A和B是否相关,MyClass 与MyClass 没有关系。MyClass 和MyClass 的公共父对象是Object。 有关在类型参数相关时,如何在两个泛型类之间创建类似子类型关系的信息,请参见 通配符和子类型


泛型类和子类型

您可以通过扩展或实现来创建泛型类或泛型接口的子类型。一个类或接口的类型形参与另一类或接口的类型形参之间的关系由extends和Implements子句确定。
以Collections类为例,ArrayList 实现List ,而List 扩展Collection 。因此ArrayList 是List 的子类型,而List 是Collection 的子类型。只要您不改变类型参数,子类型关系就保留在类型之间。
泛型,继承和子类型 - 图2
样本集合层次结构
现在假设我们要定义自己的列表接口PayloadList,该接口将泛型P的可选值与每个元素相关联。它的声明可能看起来像:

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

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

  1. PayloadList<String,String>
  2. PayloadList<String,Integer>
  3. PayloadList<String,Exception>

泛型,继承和子类型 - 图3
示例PayloadList层次结构