? extends Number 能确保集合元素均为 Number 或其子类,支持安全调用 doubleValue() 等方法;而 ? extends Object 无法保证类型约束,导致编译器无法验证类型安全性。

使用 extends 关键字配合泛型通配符(? extends T)可以安全地从集合中读取 T 及其子类的实例,但禁止写入(除 null 外),这是 Java 泛型“上界通配符”的核心设计原则。在处理数字混合集合(如 Integer、Double、BigDecimal 等)时,它能保障类型安全的只读访问。
为什么必须用 ? extends Number 而不是 ? extends Object
直接用 ? extends Object 会失去类型信息,编译器无法保证取出的对象能被当作 Number 安全使用。而 ? extends Number 告诉编译器:这个集合里所有元素都至少是 Number 类型或其子类——这意味着你可以安全调用 doubleValue()、intValue() 等方法。
-
List<? extends Number>允许传入List<Integer>、List<Double>、List<BigDecimal>等 - 但不能传入
List<String>或List<Object>(不满足“是 Number 子类”约束) - 你只能读出
Number类型引用,不能向其中 add 任意具体子类(比如add(new Integer(1))会编译失败)
实战:统一读取混合数字集合并转为 double
假设你收到多个来源的数字列表(有的是整数列表,有的是浮点列表),希望统一转换为 double 数组做计算:
public static double[] toDoubleArray(List<? extends Number> numbers) {
return numbers.stream()
.mapToDouble(Number::doubleValue)
.toArray();
}
调用示例:
-
toDoubleArray(Arrays.asList(1, 2, 3))✅(List<Integer>符合? extends Number) -
toDoubleArray(Arrays.asList(1.5, 2.7))✅(List<Double>合法) -
toDoubleArray(Arrays.asList(new BigDecimal("3.14")))✅(BigDecimal是Number子类) -
toDoubleArray(Arrays.asList("1", "2"))❌ 编译报错(String不是Number子类)
注意:不可写入,null 是唯一例外
上界通配符集合是“生产者(Producer)”,遵循 PECS 原则(Producer Extends, Consumer Super)。你不能往 List<? extends Number> 中添加任何非 null 实例:
-
list.add(new Integer(42))→ 编译错误 -
list.add(null)→ 允许(因为null可赋值给任意引用类型) - 若需写入,请改用下界通配符
? super Integer或具体类型如List<Number>
进阶:结合泛型方法增强灵活性
若想支持自定义转换逻辑(如保留精度、处理 NaN),可封装为泛型方法:
public static <T extends Number> List<Double> safeConvert(List<T> list) {
return list.stream()
.map(Number::doubleValue)
.collect(Collectors.toList());
}
该方法比通配符更进一步:它保留了原始集合的**具体类型参数**(如 T = BigDecimal),适合需要类型推导的场景;而 List<? extends Number> 更适合接收多种来源、不关心原始具体类型的聚合读取场景。

















