Generics, Inheritance, and Subtypes

众所周知,只要类型兼容,我们就可以将一种类型的对象赋值给另外一个类型的对象。举例来说,我们可以将Integer类型的对象赋值给一个Object类型,因为Object类是Integer的超类之一:

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

在面向对象术语中,我们称这种关系为is a(某某是一种/个)。因为一个Integer是一种Object,所以赋值是允许的,但是Integer也是一种Number,所以下面的代码也是成立的:

public void someMethod(Number n) { /* ... */ }

someMethod(new Integer(10));   // OK
someMethod(new Double(10.1));   // OK

在泛型中也同样成立。下面我们使泛型类型调用,Number作为类型传参,那么后续所有和Number兼容的传参都是允许的:

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>吗?答案是NO,因为Box<Integer>Box<Double>不是Box<Number>的子类型。

当使用泛型编程时会有一个普通的误解,确实一个需要理解的重要概念。

Box<Integer> is not a subtype of Box<Number> even though Integer is a subtype of Number.

给定两个具体的类型A和B(例如,NumberInteger),无论A和B是否相关,MyClass <A>MyClass <B>没有关系。MyClass <A>MyClass <B>共同的父类是Object。有关在类型参数相关时如何在两个泛型类之间创建类似子类型的关系的信息,请参考Wildcards and Subtyping

Generic Classes and Subtyping

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

这里使用Collections类作为例子,ArrayList<E>实现List<E>,而List<E>扩展Collection<E>。所以ArrayList<String>List<String>的一个子类型,List<String>则是Collection<String>的子类型。只要类型传参保持不变,子类型关系就保存在类型之间。

A sample Collections hierarchy

现在想象一下,我们想要定义我们自己的list接口PayloadList,它将泛型类型为P的可选值与每个元素相关联。它的声明如下:

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

下面这些参数化的PayloadList都是List<String>的子类:

  • PayloadList

  • PayloadList

  • PayloadList

A sample PayloadList hierarchy

Last updated

Was this helpful?