泛型,继承和子类型
众所周知,只要类型兼容,就可以将一种类型的对象分配给另一种类型的对象。例如,您可以将Integer
分配给Object
,因为Object
是Integer
的超类型之一:
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // OK
在面向对象的术语中,这称为“是”关系。由于Integer
是Object
的一种,因此允许分配。但是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\>` is not a subtype of `Box\<Number\>` even though `Integer` is a subtype of `Number` \.
Note:
给定两个具体类型A
和B
(例如Number
和Integer
),则MyClass\<A\>
与MyClass\<B\>
没有关系,无论A
和B
是否相关。 MyClass\<A\>
和MyClass\<B\>
的共同父对象是Object
。
有关在类型参数相关时如何在两个泛型类之间创建类似子类型的关系的信息,请参见通配符和子类型。
通用类和子类型
您可以通过扩展或实现来泛型通用类或interface。一个类或interface的类型参数与另一类或interface的类型参数之间的关系由extends
和implements
子句确定。
以Collections
类为例,ArrayList\<E\>
实现List\<E\>
和List\<E\> extends Collection\<E\>
。因此ArrayList\<String\>
是List\<String\>
的子类型,而List\<String\>
是Collection\<String\>
的子类型。只要您不改变类型参数,子类型关系就保留在类型之间。
Collections
层次结构示例
现在,假设我们要定义自己的列表interfacePayloadList
,它将泛型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\>
PayloadList
层次结构示例