Many Java Stream interface methods use lower bounded wildcard in parameters
for example
Stream<T> filter(Predicate<? super T> pred)
and
void forEach(Consumer<? super T> action)
What is the advantage of using Predicate<? super T>
over Predicate<T>
here?
I understand, with Predicate<? super T>
as parameter, Predicate object of T and super types can be passed into the method but i can't think of a situation where a Predicate of super type needed over the specific type?
For example if i have a Stream<Integer>
i can pass Predicate<Integer>, Predicate<Number>, and Predicate<Object>
objects as arguments to its filter method but why would anyone pass a Predicate<Object>
over Predicate<Integer>
?
What is the advantage of using <? super T>
here?
I assume you are aware of the PECS pattern, which is useful to adhere to when designing an API, even if no actual use case jumps into your eye. When we look at the final state of Java 8 and typical use cases, it’s tempting to think that this was not needed anymore. Not only do lambda expressions and method references infer the target type even when actually using a more general type, the improved type inference applies to method invocations as well. E.g.
Stream.of("a", "b", "c").filter(Predicate.isEqual("b"));
would require the filter(Predicate<? super T>)
declaration with a pre-Java 8 compiler, as it would infer Predicate<Object>
for the expression Predicate.isEqual("b")
. But with Java 8, it would also work with Predicate<T>
as parameter type, as the target type is used for nested method invocations as well.
We may consider that the development of the Stream API and the new Java language version/ compiler implementation happened at the same time, so there might have been a practical reason to use the PECS pattern at the beginning, while there never was a reason not to use that pattern. It still raises the flexibility when it comes to reusing existing predicate, function or consumer instances and even if that might not be much common, it doesn’t hurt.
Note that while, e.g. Stream.of(10, 12.5).filter(n -> n.doubleValue() >= 10)
, works, because the predicate may get an inferred non-denotable type suitable to process the Stream’s element type “#1 extends Number & Comparable<#1>
”, you can not declare a variable of that type. If you want to store the predicate in a variable, you have to use, e.g.
Predicate<Number> name = n -> n.doubleValue()>=10;
Stream.of(10, 12.5).filter(name);
which only works, if filter
has been declared as filter(Predicate<? super T> predicate)
.
Or you enforce a different element type for the Stream,
Predicate<Number> name = n -> n.doubleValue()>=10;
Stream.<Number>of(10, 12.5).filter(name);
which already demonstrates how omitting the ? super
at the filter
declaration may cause more verbosity on the caller’s side. Also, enforcing a more general element type may not be an option if the more specific type is needed in a later pipeline stage.
While existing function implementations are rare, there are some, e.g.
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();
wouldn’t work without the ? super
in the forEach(Consumer<? super T> action)
declaration.
A case you may encounter more often, is to have an existing Comparator
implementation which you might want to pass to a sorted
method of a Stream with a more specific element type, e.g,
Stream.of("FOO", "bar", "Baz")
.sorted(Collator.getInstance())
.forEachOrdered(System.out::println);
wouldn’t work without the ? super
in the sorted(Comparator<? super T> comparator)
declaration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With