127. Java 泛型 - 泛型类与子类型
1. 泛型类和接口的子类型化
在 Java 中,可以通过扩展或实现泛型类或接口来对其进行子类型化 。这意味着,如果一个类或接口是泛型的,它的类型参数将影响该类或接口的子类型关系。这个关系通过 extends
和 implements
子句来决定。
示例:ArrayList
和 List
考虑 Java 的 Collections
类库,ArrayList<E>
实现了 List<E>
,而 List<E>
又扩展了 Collection<E>
。因此,ArrayList<String>
是 List<String>
的子类型,而 List<String>
又是 Collection<String>
的子类型。这是泛型类之间子类型关系的一种体现。
java
List<String> list = new ArrayList<>(); // OK, ArrayList<String> 是 List<String> 的子类型
这就表明,当不改变类型参数时,泛型类之间会保持子类型关系。ArrayList<String>
是 List<String>
的子类型,而 List<String>
又是 Collection<String>
的子类型。
2. 自定义泛型接口的子类型化
假设我们需要定义一个自定义的泛型接口 PayloadList
,它将泛型类型 P
的值与每个元素相关联。例如,这个接口可能声明如下:
java
interface PayloadList<E, P> extends List<E> {
void setPayload(int index, P val);
// 其他方法
}
在这个接口中,E
代表列表元素的类型,P
代表与每个元素相关联的"有效负载"类型。
示例:泛型接口的子类型
接下来,我们可以创建 PayloadList
的具体类型参数化版本:
java
PayloadList<String, String> stringPayloadList = new SomePayloadListImpl<>();
PayloadList<String, Integer> integerPayloadList = new SomePayloadListImpl<>();
PayloadList<String, Exception> exceptionPayloadList = new SomePayloadListImpl<>();
PayloadList<String, String>
是List<String>
的子类型。PayloadList<String, Integer>
是List<String>
的子类型,尽管它使用了不同的泛型类型参数。PayloadList<String, Exception>
也是List<String>
的子类型,尽管它的第二个类型参数是Exception
。
解释
这里的核心概念是:只要不改变类型参数之间的相对关系,泛型类型之间就能保留子类型关系 。即使我们在泛型接口 PayloadList
中使用不同的类型参数,它们依然可以和 List<String>
保持子类型关系。关键是,类型参数 E
的一致性确保了我们可以将 PayloadList<E, P>
类型的对象赋值给 List<E>
类型的变量。
3. 泛型类和接口的类型参数
泛型类和接口的类型参数与它们的子类型关系密切相关。类型参数不仅决定了接口或类的功能,还影响了它们的继承关系和泛型约束。例如,不能将 PayloadList<String, String>
直接赋值给 PayloadList<Integer, Integer>
,即使这两个类型都继承自 List<String>
,因为它们的第二个类型参数不同。
java
PayloadList<String, String> list1 = new SomePayloadListImpl<>();
PayloadList<Integer, Integer> list2 = new SomePayloadListImpl<>();
// 这将会报错,因为 list1 和 list2 不是相同类型,即使它们都继承自 List<String>。
list1 = list2; // 编译错误,类型参数不同
4. 总结
- 泛型类和接口的子类型化 :泛型类或接口的子类型关系由类型参数确定。只要不改变类型参数之间的关系,子类型关系就能保留。例如,
ArrayList<String>
是List<String>
的子类型,而List<String>
又是Collection<String>
的子类型。 - 自定义泛型接口 :我们可以定义自己的泛型接口,如
PayloadList
,并通过不同的类型参数创建不同的子类型。这些子类型之间仍然保持继承关系。 - 类型参数的关系:泛型类或接口的类型参数决定了它们的继承关系和可赋值性。类型参数不匹配的泛型类型不能互相赋值。
通过理解这一点,学员可以更好地理解泛型类和接口之间的继承关系,以及如何自定义泛型接口和确保类型安全。