许多Java Stream接口方法在参数
中使用下限有界通配符例如
Stream<T> filter(Predicate<? super T> pred)
和
void forEach(Consumer<? super T> action)
在这里使用Predicate<? super T>
超过Predicate<T>
有什么好处?
我理解,以Predicate<? super T>
为参数,T和超类型的Predicate对象可以传递给方法,但是我不能想到超类型谓词超过特定类型的情况?
例如,如果我有Stream<Integer>
我可以将Predicate<Integer>, Predicate<Number>, and Predicate<Object>
个对象作为其过滤方法的参数传递,但为什么有人会将Predicate<Object>
传递给Predicate<Integer>
?
在这里使用<? super T>
有什么好处?
答案 0 :(得分:5)
我假设您了解PECS模式,这在设计API时非常有用,即使没有实际的用例会突然出现。当我们查看Java 8的最终状态和典型用例时,很容易认为不再需要它了。即使实际使用更通用的类型,lambda表达式和方法引用不仅会推断目标类型,而且改进的类型推断也适用于方法调用。 E.g。
Stream.of("a", "b", "c").filter(Predicate.isEqual("b"));
需要使用Java 8之前的编译器进行filter(Predicate<? super T>)
声明,因为它会推断Predicate<Object>
表达式Predicate.isEqual("b")
。但是对于Java 8,它也可以使用Predicate<T>
作为参数类型,因为目标类型也用于嵌套方法调用。
我们可能会认为Stream API的开发和新的Java语言版本/编译器实现同时发生,因此可能有一个实际的理由在开始时使用PECS模式,而从来没有不使用该模式的原因。在重用现有的谓词,函数或消费者实例时,它仍然提高了灵活性,即使这可能不太常见,也不会受到伤害。
请注意,例如, Stream.of(10, 12.5).filter(n -> n.doubleValue() >= 10)
,因为谓词可能得到一个适合处理Stream的元素类型“#1 extends Number & Comparable<#1>
”的推断的非可表示类型,你不能声明该类型的变量。如果要将谓词存储在变量中,则必须使用,例如
Predicate<Number> name = n -> n.doubleValue()>=10;
Stream.of(10, 12.5).filter(name);
仅在filter
被声明为filter(Predicate<? super T> predicate)
时才有效。
或者您为Stream强制执行不同的元素类型,
Predicate<Number> name = n -> n.doubleValue()>=10;
Stream.<Number>of(10, 12.5).filter(name);
已经演示了如何在? super
声明中省略filter
可能会导致调用方更加冗长。此外,如果在以后的管道阶段需要更具体的类型,则可能不会强制执行更通用的元素类型。
虽然现有的功能实现很少,但也有一些,例如
Stream.Builder<Number> b = Stream.builder();
IntStream.range(0, 10).boxed().forEach(b);
LongStream.range(0, 10).boxed().forEach(b);
Stream<Number> s = b.build();
如果没有? super
声明中的forEach(Consumer<? super T> action)
,将无效。
您可能经常遇到的情况是,您可能希望将现有的Comparator
实现传递给具有更具体元素类型的Stream的sorted
方法,例如
Stream.of("FOO", "bar", "Baz")
.sorted(Collator.getInstance())
.forEachOrdered(System.out::println);
如果没有? super
声明中的sorted(Comparator<? super T> comparator)
,将无效。