Я предполагаю, что вы знаете о шаблоне PECS , который полезно придерживаться при разработке API, даже если ни один фактический вариант использования не бросается вам в глаза.Когда мы смотрим на конечное состояние Java 8 и типичные варианты использования, возникает соблазн думать, что это больше не нужно.Лямбда-выражения и ссылки на методы не только выводят целевой тип, даже когда фактически используется более общий тип, но и улучшенный вывод типа применяется и к вызовам методов.Например,
Stream.of("a", "b", "c").filter(Predicate.isEqual("b"));
потребует объявления filter(Predicate<? super T>)
с компилятором до Java 8, поскольку для выражения Predicate.isEqual("b")
будет выведено Predicate<Object>
.Но в Java 8 он также будет работать с Predicate<T>
в качестве типа параметра, поскольку целевой тип также используется для вызова вложенных методов.
Мы можем считать, что разработка Stream API и новой JavaРеализация языковой версии / компилятора произошла в одно и то же время, поэтому могла существовать практическая причина использовать шаблон PECS в начале, хотя никогда не было причины не использовать этот шаблон.Это все еще повышает гибкость, когда дело доходит до повторного использования существующих экземпляров предикатов, функций или потребителей, и даже если это не так часто встречается, это не повредит.
Обратите внимание, что хотя, например, Stream.of(10, 12.5).filter(n -> n.doubleValue() >= 10)
, работает,поскольку предикат может получить предполагаемый неденазванный тип, подходящий для обработки типа элемента потока «#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)
.Или вы применяете другой тип элемента для потока,
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
, которую вы, возможно, захотите передатьsorted
метод потока с более конкретным типом элемента, например,
Stream.of("FOO", "bar", "Baz")
.sorted(Collator.getInstance())
.forEachOrdered(System.out::println);
не будет работать без ? super
в объявлении sorted(Comparator<? super T> comparator)
.