文章目录
- 134. Java 泛型 - 上限通配符 (`? extends T`)
- **1. 什么是上限通配符 (`? extends T`)?**
- **2. 为什么使用 `? extends T`?**
- **3. 示例:使用 `? extends T` 进行数据读取**
- **✅ 示例 1:计算数值列表的总和**
- **4. 注意事项:为什么不能向 `List<? extends T>` 添加元素?**
- **❌ 错误示例**
- **5. 适用场景**
- **6. 结论**
134. Java 泛型 - 上限通配符 (? extends T
)
在 Java 泛型中,我们可以使用上限通配符 (? extends T
) 来放宽对变量的限制,使方法能够适用于某个类型及其所有子类型。
让我们深入了解它的概念,并通过示例加深理解。
1. 什么是上限通配符 (? extends T
)?
在泛型中,我们可以使用 ? extends T
来表示“某个类 T 及其所有子类”。
- 其中,
extends
在这里不仅表示类的继承,也可以用于接口的实现。 - 这在处理同一类层级中的不同泛型类型时非常有用。
✅ 语法
List<? extends Number> list;
list
可以接受List<Number>
、List<Integer>
、List<Double>
、List<Float>
等。- 但是,不能向
list
中添加元素(除了null
),因为 Java 无法确定具体的子类型。
2. 为什么使用 ? extends T
?
假设我们需要编写一个方法,它能够接受 List<Integer>
、List<Double>
和 List<Number>
,而不需要为每个具体类型单独编写方法。
使用 ? extends T
可以让方法适用于T 及其子类,避免代码重复,增强通用性。
3. 示例:使用 ? extends T
进行数据读取
📌 需求:编写一个方法计算 List<Number>
及其子类(如 Integer
、Double
)的总和。
✅ 示例 1:计算数值列表的总和
import java.util.Arrays;
import java.util.List;public class UpperBoundWildcard {public static double sumOfList(List<? extends Number> list) {double sum = 0.0;for (Number n : list) {sum += n.doubleValue(); // ✅ 可以安全地调用 Number 的方法}return sum;}public static void main(String[] args) {List<Integer> integers = Arrays.asList(1, 2, 3);System.out.println("Sum of integers: " + sumOfList(integers)); // 输出: 6.0List<Double> doubles = Arrays.asList(1.2, 2.3, 3.5);System.out.println("Sum of doubles: " + sumOfList(doubles)); // 输出: 7.0}
}
🔍 解析
sumOfList(List<? extends Number> list)
接受Number
及其所有子类。list
可以是List<Integer>
、List<Double>
、List<Float>
等。n.doubleValue()
✅ 安全调用Number
类的方法,因为所有Number
的子类都继承了doubleValue()
。
4. 注意事项:为什么不能向 List<? extends T>
添加元素?
⚠ 在 List<? extends T>
中,我们无法添加元素(除了 null
),否则会导致编译错误。
❌ 错误示例
List<? extends Number> list = new ArrayList<Integer>();
list.add(5); // ❌ 编译错误
list.add(3.14); // ❌ 编译错误
🔍 为什么?
list
可能是List<Integer>
,但list.add(3.14)
试图添加Double
,这会破坏List<Integer>
的类型安全性。- Java 编译器无法确保
list
的实际类型,因此不允许添加元素,但可以安全读取元素(因为所有元素都是Number
)。
✅ 解决方案
List<Number> list = new ArrayList<>();
list.add(5); // ✅ 允许
list.add(3.14); // ✅ 允许
如果需要向列表添加元素,就不能使用 ? extends T
,而应该使用具体类型 T
。
5. 适用场景
✅ 适用于只“读取”数据的情况(Producer)。
✅ 适用于同一继承层级中的多个泛型类型(如 Number
的子类)。
✅ 适用于通用方法,使代码更具灵活性。
6. 结论
✅ ? extends T
表示 T 及其所有子类,适用于“读取”数据的情况。
✅ 无法向 List<? extends T>
添加元素,因为 Java 无法确保其实际类型。
✅ 使用 ? extends T
可以增强代码的通用性,避免代码重复。
✅ 典型用法:计算数值列表的总和,遍历对象并调用父类方法。
🎯 记住口诀:
- Producer Extends (
? extends T
) 👉 用于“读取”数据 - Consumer Super (
? super T
) 👉 用于“写入”数据
🚀 泛型让 Java 代码更灵活,学会 ? extends T
,让你的代码更通用!