Почему Optional.map помогает выполнить это задание?

Optional<ArrayList<String>> option = Optional.of(new ArrayList<>());

Optional<ArrayList<?>> doesntWork = option;

Optional<ArrayList<?>> works = option.map(list -> list);

Первое присваивание не компилируется, а второе с map компилируется. Такое ощущение, что map на самом деле ничего не должен делать, но по какой-то причине он превращает мой Optional<ArrayList<String>> в Optional<ArrayList<?>>. Происходит ли какое-то неявное приведение?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
36
0
1 812
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

У вашего option.map есть подпись

<ArrayList<?>> Optional<ArrayList<?>> java.util.Optional.map(Function<? super ArrayList<String>, ? extends ArrayList<?>> mapper)

Так это

Optional<? extends ArrayList<?>> doesntWork = option;

компилируется.

Да, это компилируется, но меня больше интересует, почему карта это исправляет, это просто упрощенная версия того, что я на самом деле пытаюсь сделать. В моем фактическом коде я переопределяю метод, который должен возвращать эквивалент Optional<ArrayList<?>>, поэтому простое изменение типа этого присваивания на самом деле не помогает.

Carl Minden 04.04.2019 10:22

В вашем последнем случае тип возвращаемого значения метода Optional.map неявно определяется типом вашей переменной works. Вот почему есть разница.

Но чем это отличается от первого задания? если сигнатура карты определяется объявлением типа присваивания для возврата Optional<ArrayList<?>>, то это означает, что возвращаемая лямбда будет иметь тип ArrayList<?> и поскольку моя лямбда (по крайней мере, как насколько я могу судить), возвращая конкретный тип ArrayList<String>, что-то где-то отбрасывает его постфактум, я думаю?

Carl Minden 04.04.2019 10:24

Да, точно. Разница в том, что тип option является статическим, а возвращаемый тип Optional.map динамически определяется присваиваемой переменной.

dpr 04.04.2019 10:42
Ответ принят как подходящий

Если вы посмотрите на код map и проследите за вызовами всех методов, вы увидите, что option.map(list -> list) в конечном итоге возвращает new Optional<>(option.get()). Таким образом, вы можете заменить свое последнее задание на:

Optional<ArrayList<?>> works = new Optional<>(option.get());

Это создает новый Optional<ArrayList<?>> и инициализирует его переменную экземпляра value (тип которой ArrayList<?>) с ArrayList<String>, возвращаемым map.get(). Это верное назначение.

Is there some sort of implicit cast going on?

Нет, map возвращает новый экземпляр Optional. Он не приводит исходный экземпляр, для которого он был вызван.

Вот цепочка вызовов методов:

option.map(list -> list)

возвращает (поскольку option не пусто)

Optional.ofNullable(mapper.apply(value))

что в вашем случае совпадает с

Optional.ofNullable(value)

который возвращает (поскольку значение не равно нулю):

Optional.of(value)

который возвращает

new Optional<>(value)

Интересно, просто кажется таким странным, что .map(list -> list) что-то делает, но да, я полагаю, в этом есть смысл.

Carl Minden 04.04.2019 10:26

См. также этот ответ. В последнем примере кода используется map(Function.identity()), где прямое присвоение Optional невозможно. В контексте этого вопроса A будет ArrayList<?>, а B будет ArrayList<String>.

Holger 04.04.2019 12:31

Ну, первый не работает, потому что дженерики инвариантны, единственный способ сделать их ковариантными — добавить, например, ограниченный тип:

 Optional<? extends ArrayList<String>> doesntWork = option; 

что бы скомпилировать.

И когда вы говорите, что шаг map ничего не должен делать, это неправильно. Посмотрите на определение Optional::map:

public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent()) {
        return empty();
    } else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

грубо говоря, это делает преобразуется из Optional<T> в Optional<U>...

Другие вопросы по теме