Java泛型? extends通配符赋值编译规则相关问题
Java泛型常见编译问题解答
问题1:上界通配符的赋值规则
代码含义
List<? extends Exception> lst = new ArrayList<Exception>();声明了一个元素类型带上界通配符的List引用:
? extends Exception的约束是:这个引用指向的List,持有的元素必须是Exception本身或其任意子类,但编译器不会记录具体的元素子类型。- 这行代码能编译通过的核心原因是:右侧的
ArrayList<Exception>的泛型参数Exception完全落在? extends Exception的允许范围内(上界包含边界类型自身),同时ArrayList实现了List接口,符合引用赋值的类型匹配要求。
反例编译失败原因
List<NullPointerException> lst2 = new ArrayList<Exception>();无法编译,是Java泛型的不变性决定的:
- 即使
NullPointerException是Exception的子类,List<NullPointerException>和List<Exception>也不存在继承关系,是完全独立的两种类型。 - 如果允许这种赋值,会直接破坏类型安全:你可以通过底层
ArrayList<Exception>的引用往集合里存入IOException等其他Exception子类,但lst2的声明类型要求取出的元素必须是NullPointerException,读取时会直接触发类型转换异常,因此编译器会直接拦截这类无通配符的跨泛型参数赋值。
额外说明:给List<? extends Exception>类型的引用赋值后,编译器会禁止你往集合里添加除null之外的任何元素——因为不知道具体元素类型,从根源上避免了类型污染,这也是上界通配符赋值安全的底层保障。
问题2:泛型集合引用赋值的合法性判断
你给出的代码如下:
public class examsQ<S> { public void f3(Collection<? extends S> c, List<S> l ) { c = l; } }
这段代码能编译通过,完全符合Java泛型的类型规则,逻辑非常清晰:
- 首先是原始类型的匹配:
List是Collection的子接口,List<S>本身就是Collection<S>的子类型,满足接口引用赋值的基本要求。 - 其次是泛型参数的匹配:
? extends S表示“可以接受S本身或S的任意子类作为泛型参数”,S本身当然在允许范围内,因此Collection<S>属于Collection<? extends S>的合法子类型。
这里的常见误区是误以为? extends S代表“集合只能存储S的子类,不能存储S本身”,实际上上界通配符的约束是集合里的元素都可以安全向上转型为S,List<S>里存储的都是S类型的元素,完全满足这个约束。赋值后编译器同样会限制往c里添加非null元素,不会出现类型安全问题,因此编译可以正常通过。
内容的提问来源于stack exchange,提问作者JavaCodersince2022
相关产品推荐
相关产品推荐

