Почему подстановочные знаки параметров типа Optional или методов FlatMap являются символами подстановки?
Метод Optional.or
был добавлен в Java 9. Это подпись метода
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
Почему берется параметр типа Supplier
? extends Optional
? extends Optional
а не просто Optional
, так как Optional
является окончательным классом?
То же самое верно для метода Optional.flatMap
. Это изменение от Java 8.
В Java 8 это была Function<? super T, Optional<U>> mapper
Function<? super T, Optional<U>> mapper
тогда как он был изменен на Function<? super T,? extends Optional<? extends U>>
Function<? super T,? extends Optional<? extends U>>
Function<? super T,? extends Optional<? extends U>>
в Java 9.
Ответы
Ответ 1
Я нашел причину этого от самого Стюарта Маркса
http://mail.openjdk.java.net/pipermail/core-libs-dev/2016-October/044026.html
Это связано с вложенными обобщениями (Optional
, вложено в Function
). Из почтовой ветки
Function<..., Optional<StringBuilder>>
не является подтипом
Function<..., Optional<? extends CharSequence>>
Чтобы обойти это, мы должны добавить внешний подстановочный знак, так что
Function<..., Optional<StringBuilder>>
это подтип
Function<..., ? extends Optional<? extends CharSequence>>
Ответ 2
Да... говорят, что подстановочный знак с границей с расширением (верхней границей) делает тип ковариантным, что означает, что, например, List<Apple>
является фактическим подтипом List<? extends Fruit>
List<? extends Fruit>
(учитывая, что Apple
расширяет Fruit
); это также называется ковариацией.
Или в приведенных вами примерах это означает, что Optional<StringBuilder>
является подтипом Optional<? extends Optional<? extends CharSequence>>
Optional<? extends Optional<? extends CharSequence>>
Optional<? extends Optional<? extends CharSequence>>
, так что вы можете, например, сделать:
List<Optional<String>> left = new ArrayList<>();
List<? extends Optional<? extends CharSequence>> right = new ArrayList<>();
right = left; // will compile
или назначить Function
другому
Ответ 3
FWIW, аналогичная проблема с ковариантными аргументами все еще существует в Stream.iterate и Stream.iterate в Java 11. Текущие сигнатуры методов
static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
Эти подписи не допускают некоторых комбинаций семени и UnaryOperator
которые являются звуковыми с точки зрения типа, например, следующее не компилируется:
UnaryOperator<String> op = s -> s;
Stream<CharSequence> scs = iterate("", op); // error
Предложенное решение заключается в изменении метода подписи на
static <T, S extends T> Stream<T> iterate(S seed, Predicate<? super S> hasNext, UnaryOperator<S> next)
static <T, S extends T> Stream<T> iterate(S seed, UnaryOperator<S> f)
Таким образом, в отличие от Optional.or и Optional.flatMap, в этом случае "подход с дополнительным параметром типа" действительно работает.