137. Java 泛型 - 泛型与通配符:子类型化关系
文章目录
- 137. Java 泛型 - 泛型与通配符:子类型化关系
- **1. 常规类的继承与泛型类型的关系**
- **2. 泛型类型与子类型化**
- **❌ 错误示例:**
- **3. 使用通配符解决泛型之间的子类型关系**
- **✅ 示例:`List<Integer>` 和 `List<Number>` 之间的关系**
- **4. 上限通配符与下限通配符的关系**
- **上限通配符(`? extends T`)**
- **下限通配符(`? super T`)**
- **5. 使用通配符创建泛型类型之间的关系**
- **6. 总结**
- **通配符与泛型子类型化的要点:**
- **通配符的实际应用:**
- **7. 小贴士:通配符的使用场景**
137. Java 泛型 - 泛型与通配符:子类型化关系
在 Java 中,泛型类和接口并不像普通类那样直接遵循继承规则。为了解决这个问题,我们可以使用通配符 (? extends T
和 ? super T
) 来创建泛型类或接口之间的关系。本篇将详细解释如何使用通配符建立泛型类型之间的子类型关系,并通过示例帮助您理解。
1. 常规类的继承与泛型类型的关系
在普通类中,继承遵循子类型规则,即如果类 B
扩展类 A
,则 B
是 A
的子类型。您可以这样编写代码:
class A { /* ... */ }
class B extends A { /* ... */ }B b = new B();
A a = b; // 合法,因为 B 是 A 的子类
🔍 子类型规则:
B
是A
的子类型,您可以将B
类型的对象赋给A
类型的变量。
2. 泛型类型与子类型化
与常规类不同,泛型类型之间并不直接遵循子类型化规则。这意味着,即使 Integer
是 Number
的子类,List<Integer>
也不是 List<Number>
的子类型,实际上这两者之间没有任何继承关系。
❌ 错误示例:
List<B> lb = new ArrayList<>();
List<A> la = lb; // 编译错误:List<B] 不是 List<A> 的子类型
即使 B
是 A
的子类,List<B>
并不等于 List<A>
,这会导致编译错误。
3. 使用通配符解决泛型之间的子类型关系
为了建立泛型类型之间的关系,可以使用上限通配符 (? extends T
) 来让 Java 编译器理解某个泛型类型是另一个泛型类型的子类型。
✅ 示例:List<Integer>
和 List<Number>
之间的关系
Integer
是 Number
的子类型,但是 List<Integer>
不是 List<Number>
的子类型。这是因为 泛型类型的子类型化并不自动传递。我们可以通过上限通配符来为 List<Integer>
和 List<Number>
之间建立关系。
List<? extends Integer> intList = new ArrayList<>();
// 这是合法的,因为 List<? extends Integer> 是 List<? extends Number> 的子类型
List<? extends Number> numList = intList;
🔍 解析:
intList
是List<? extends Integer>
,它表示存储了Integer
或其子类的列表。numList
是List<? extends Number>
,它表示存储了Number
或其子类的列表。- 由于
Integer
是Number
的子类,所以List<? extends Integer>
也可以看作List<? extends Number>
的子类型。这样,我们就可以建立起intList
和numList
之间的关系。
4. 上限通配符与下限通配符的关系
上限通配符(? extends T
)
- 用途:用来指定类型范围,表示元素的类型是
T
或T
的子类型。 - 适用场景:适用于读取元素(数据消费)场景,您只能读取
T
类型的数据,不能插入(除了null
)。
下限通配符(? super T
)
- 用途:用来指定类型范围,表示元素的类型是
T
或T
的超类型。 - 适用场景:适用于写入元素(数据生产)场景,您可以将
T
类型的数据插入列表中,但只能作为Object
类型读取元素。
5. 使用通配符创建泛型类型之间的关系
通过通配符,我们可以创建泛型类型之间的关系,使得代码更加灵活。以下是一些关键的泛型类型关系:
List<? extends Number>
可以由List<Integer>
、List<Double>
等列表来填充,因为它们都是Number
的子类。List<? super Integer>
可以由List<Integer>
、List<Number>
、List<Object>
来填充,因为Integer
是这些类型的父类。
List<? extends Number> numList = new ArrayList<Integer>(); // 合法,因为 Integer 是 Number 的子类
List<? super Integer> objList = new ArrayList<Number>(); // 合法,因为 Integer 是 Number 的子类
6. 总结
通配符与泛型子类型化的要点:
- 普通类的继承:遵循子类型规则,如果
B
扩展A
,则B
是A
的子类型。 - 泛型类的继承:泛型类型之间没有直接的继承关系,
List<B>
并不是List<A>
的子类型。 - 使用通配符(
? extends T
和? super T
):? extends T
:表示“某个类型T
或T
的子类型”,适用于数据读取(消费数据)。? super T
:表示“某个类型T
或T
的超类型”,适用于数据写入(生产数据)。
通配符的实际应用:
- 建立泛型类型之间的关系,让代码更加灵活。
- 使用通配符来处理不同类型的泛型数据,实现更广泛的类型兼容性。
7. 小贴士:通配符的使用场景
? extends T
:适用于 读取数据 时需要限制元素类型范围的情况,数据消费者。? super T
:适用于 写入数据 时需要限制元素类型范围的情况,数据生产者。
🎯 记住:
- 泛型类型之间并没有直接的继承关系,但通配符可以帮助建立这些关系,使得代码更灵活。