Java泛型通配符使用报错:<? extends Number>类型List调用add方法失败
错误原因分析
上界通配符? extends Number表示集合的实际泛型类型是Number的某个未知子类型,编译器无法确定这个具体类型是什么,为了保证类型安全,会禁止向集合中写入任何非null的元素:比如你传入的可能是List<Integer>,也可能是List<Double>,如果允许写入2,那传入List<Double>的时候就会出现类型错误,所以编译器直接在编译阶段拦截了add操作。
你看到的报错信息里的CAP#1就是编译器捕获的通配符对应的未知实际类型,它无法确认int类型的2可以转换为这个未知类型,所以抛出类型不匹配的错误。
代码优化方案
以下是三种常见场景的优化方案:
场景1:方法仅需读取集合元素
如果你的runtest方法不需要写入元素,仅需要读取元素做处理,? extends Number的定义是正确的,只需删除写入逻辑即可:
import java.util.*; public class AnotherClass { public void runtest(List<? extends Number> a){ // 仅做读取操作是合法的 for (Number num : a) { System.out.println("数值:" + num); } } }
场景2:方法需要写入集合元素
如果需要向集合中写入Number及其子类型的元素,应该把上界通配符替换为下界通配符? super Number:
import java.util.*; public class AnotherClass { public void runtest(List<? super Number> a){ a.add(2); // 写入Integer类型合法 a.add(3.14); // 写入Double类型也合法 } }
下界通配符
? super T表示集合的实际泛型类型是T或者T的父类,所以写入T及其子类型的元素时,都可以安全向上转型,编译器不会拦截。
场景3:需要读写类型完全匹配
如果需要保证读写的元素类型完全一致,建议使用泛型方法替代通配符:
import java.util.*; public class AnotherClass { public <T extends Number> void runtest(List<T> a, T element){ a.add(element); // 写入的元素类型和集合泛型完全匹配 } }
调用时传入对应类型的元素即可:
another.runtest(a1, 2);
泛型通配符使用规则
通用的使用原则为PECS:
- Producer Extends:如果集合作为生产者,只需要向外提供元素(仅读),使用
? extends T上界通配符 - Consumer Super:如果集合作为消费者,只需要接收元素写入(仅写),使用
? super T下界通配符 - 如果集合同时需要读写操作,不要使用通配符,直接指定明确的泛型类型
内容的提问来源于stack exchange,提问作者Mohit Yadav
相关产品推荐
相关产品推荐

