Почему Optional.map заставляет это назначение работать? - PullRequest
36 голосов
/ 04 апреля 2019
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<?>>.Происходит ли какое-то неявное приведение?

Ответы [ 4 ]

25 голосов
/ 04 апреля 2019

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

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

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

Есть ли какое-то неявное приведение в действие?

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

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

option.map(list -> list)

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

Optional.ofNullable(mapper.apply(value))

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

Optional.ofNullable(value)

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

Optional.of(value)

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

new Optional<>(value)
10 голосов
/ 04 апреля 2019

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

 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> ...

0 голосов
/ 04 апреля 2019

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

0 голосов
/ 04 апреля 2019

Ваш option.map имеет подпись

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

Итак, это

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

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

...