Почему подстановочные знаки параметров типа 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, в этом случае "подход с дополнительным параметром типа" действительно работает.