? extends T 表示容器元素为 T 或其子类,编译器仅保证安全读取(可赋值给 T),禁止写入(除 null 外);它提供协变视图,解决泛型不变性导致的类型兼容问题。

Java 中用 ? extends T 限制泛型,不是为了“让子类能传入”,而是告诉编译器:“这个容器里装的一定是 T 或它的某个子类,但具体是哪一个,我不确定”。所以它限制的是实际类型必须是 T 的子类型(含 T 自身),而不是限制方法参数或变量声明时能写哪些子类。
为什么用 ? extends T 能接受子类?
因为 Java 泛型是不变的(invariant),List<integer></integer> 和 List<number></number> 没有父子关系。但通配符 ? extends T 提供了一种安全的“上协变”视图:
-
List extends Number>可以指向new ArrayList<integer>()</integer> -
List extends Number>可以指向new ArrayList<double>()</double> -
List extends Number>也可以指向new ArrayList<number>()</number> - 但不能指向
new ArrayList<string>()</string>(编译报错)
它真正限制的是“能做什么”,不是“能声明什么”
关键不在“谁可以赋值给它”,而在“你对它能安全执行哪些操作”:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- ✅ 安全读取:get() 返回值可赋给
Number或其父类(如Object),因为编译器保证它至少是Number - ❌ 禁止写入:add()、set() 全部被禁止——哪怕传
Integer也不行,因为真实容器可能是ArrayList<double></double>,写入整数会破坏类型安全
常见误用与正解
有人想用 ? extends T 来“接收任意子类对象”,比如写一个方法处理所有 Animal 子类:
- 错误写法:
void handle(List extends Animal> list)—— 这没问题,但若你在里面 try add(new Dog()) 就会编译失败 - 正确思路:如果要写入,应改用
? super Animal;如果只遍历/读取,? extends Animal正合适 - 注意:声明
List extends Animal> animals = new ArrayList<dog>()</dog>是合法的,但animals.add(new Cat())不合法
和泛型类声明中的 的区别
这是两个不同场景,别混用:
-
<t extends number></t>出现在类/方法定义中,表示“这个 T 必须是 Number 的子类”,用于约束类型形参本身,允许你在方法体内调用doubleValue()等方法 -
? extends Number出现在变量、参数或字段声明中,表示“这个引用所指的实际类型是未知的 Number 子类”,不提供具体类型能力,只提供安全读取保障
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










