Java Stream中通配符与PECS原则的正确使用及Predicate适配疑问
关于Java Stream.filter中Predicate通配符的PECS应用
先看你的代码:
Predicate<Object> isPositive = n -> (Integer) n > 0; Stream.of(-2, 5, 8).filter(isPositive);
首先要明确Stream.filter的官方方法签名是Stream<T> filter(Predicate<? super T> predicate),这里用下界通配符<? super T>而非上界<? extends T>,完全符合PECS原则,拆解分析:
- 对于
Stream<T>来说,filter方法里的Predicate是消费者:它接收流中的T类型元素并做判断。按照PECS规则,消费者要使用super——Predicate<? super T>可以接受任何能消费T的谓词,包括Predicate<T>、Predicate<Number>、Predicate<Object>这类针对T父类型的实现。
比如你的例子里,流是Stream<Integer>,Predicate<Object>能传入是因为Object是Integer的父类,谓词可以接收Integer类型元素(向上转型为Object);如果换成Predicate<Integer>,同样符合要求,因为Integer本身属于<? super Integer>的兼容范围。
如果反过来用<? extends T>,比如Predicate<? extends Integer>,由于Integer是final类,只能传入Predicate<Integer>;换成非final类场景,这种限制会大幅降低谓词复用性——比如Predicate<Number>就无法用于Stream<Integer>的过滤,而实际上Number的谓词完全能处理Integer元素,这显然不合理。
回到你的代码,其实没必要用Predicate<Object>,直接用Predicate<Integer>更安全,还能避免强制类型转换:
Predicate<Integer> isPositive = n -> n > 0; Stream.of(-2, 5, 8).filter(isPositive);
这段代码完全符合Predicate<? super Integer>的参数要求,编译无任何问题。
总结:
- Stream作为生产者是指它生成元素供外部使用,但
filter里的Predicate是消费流元素的角色,因此遵循PECS的「Consumer用super」原则。 - 使用
<? super T>能让Predicate的适用范围更广,不管是针对T本身还是T父类型的谓词,都能用来过滤流中的T类型元素。
内容的提问来源于stack exchange,提问作者Mohan Bandlapalli
相关产品推荐
相关产品推荐

