为何Predicate<? extends Number>编译报错?Collection泛型却正常
问题解析
核心原因:Predicate的参数消费特性与通配符误用
你对Collection<? extends Number>的理解是准确的:它表示集合中的元素是Number或其子类,遍历用Number接收是安全的——任何子类都可以向上转型为父类Number。
但Predicate<? extends Number>的逻辑完全不同:
Predicate<T>的test(T t)方法要求传入参数为T类型。当你声明Predicate<? extends Number>时,这个通配符意味着该Predicate可能是Predicate<Integer>、Predicate<Double>或Predicate<Number>中的任意一种。- 假设实际传入的是
Predicate<Integer>,它的test方法仅接受Integer类型参数,但你传入的i是Number类型——Number是Integer的父类,无法安全向下转型为Integer,编译器无法保证类型安全,因此直接报错。
简言之:Collection<? extends Number>是产出元素的场景,协变通配符? extends适配这种场景;而Predicate是消费参数的场景,消费场景需要用逆变通配符? super。
修正方案
将Predicate的参数类型改为Predicate<? super Number>,确保test方法能接受Number及其所有子类的参数:
public int countSpecial(Collection<? extends Number> input, Predicate<? super Number> p) { int count = 0; for (Number i : input) { if (p.test(i)) { count++; } } return count; }
另外,方法上的泛型声明<T extends Collection<? extends Number>>是多余的(方法内未使用T类型),直接删除即可。
补充:PECS原则
这个问题本质对应Java泛型的PECS原则:
- Producer Extends:若对象是生产者(产出元素,如Collection遍历),使用
? extends; - Consumer Super:若对象是消费者(接收元素,如Predicate的test方法),使用
? super。
内容的提问来源于stack exchange,提问作者Ankit Ghildiyal
相关产品推荐
相关产品推荐

