为什么许多Java Stream接口方法在参数中使用下界通配符而不是通用类型?

Arj*_*jun 3 java generics java-stream

许多Java Stream接口方法在参数中使用下界通配符

例如

Stream<T> filter(Predicate<? super T> pred)
Run Code Online (Sandbox Code Playgroud)

void forEach(Consumer<? super T> action)
Run Code Online (Sandbox Code Playgroud)

Predicate<? super T>Predicate<T>这里使用的好处是什么?

我知道,Predicate<? super T>可以将T和超级类型的谓词对象作​​为参数传递给方法,但是我无法想到在特定类型上需要超级类型的谓词的情况?

例如,如果我有一个Stream<Integer>i,则可以将Predicate<Integer>, Predicate<Number>, and Predicate<Object>对象作为参数传递给它的filter方法,但是为什么有人会传递Predicate<Object>over Predicate<Integer>?在这里

使用的好处是<? super T>什么?

Hol*_*ger 5

我假设您知道PECS模式,即使没有实际用例进入您的视线,在设计API时也要遵守该模式。当我们查看Java 8的最终状态和典型用例时,很容易以为不再需要它了。即使实际上使用更通用的类型时,lambda表达式和方法引用不仅可以推断目标类型,而且改进的类型推断也适用于方法调用。例如

Stream.of("a", "b", "c").filter(Predicate.isEqual("b"));
Run Code Online (Sandbox Code Playgroud)

将需要filter(Predicate<? super T>)使用Java 8之前的编译器进行声明,因为它将推断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);
Run Code Online (Sandbox Code Playgroud)

仅当filter被声明为时才有效filter(Predicate<? super T> predicate)。或者您为Stream强制使用其他元素类型,

Predicate<Number> name = n -> n.doubleValue()>=10;
Stream.<Number>of(10, 12.5).filter(name);
Run Code Online (Sandbox Code Playgroud)

这已经演示了如何省略? superfilter声明可能对主叫方的一侧更冗长。另外,如果在后续的管道阶段中需要更具体的类型,则强制执行更一般的元素类型可能不是一种选择。

现有功能的实现很少见,但也有一些实现,例如

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();
Run Code Online (Sandbox Code Playgroud)

就没有不行? superforEach(Consumer<? super T> action)声明。

您可能经常遇到的一个情况是拥有一个现有的Comparator实现,您可能希望将该实现传递给sorted具有更特定元素类型的Stream方法,例如,

Stream.of("FOO", "bar", "Baz")
      .sorted(Collator.getInstance())
      .forEachOrdered(System.out::println);
Run Code Online (Sandbox Code Playgroud)

就没有不行? supersorted(Comparator<? super T> comparator)声明。