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<?>>. Происходит ли какое-то неявное приведение?




У вашего option.map есть подпись
<ArrayList<?>> Optional<ArrayList<?>> java.util.Optional.map(Function<? super ArrayList<String>, ? extends ArrayList<?>> mapper)
Так это
Optional<? extends ArrayList<?>> doesntWork = option;
компилируется.
В вашем последнем случае тип возвращаемого значения метода Optional.map неявно определяется типом вашей переменной works. Вот почему есть разница.
Но чем это отличается от первого задания? если сигнатура карты определяется объявлением типа присваивания для возврата Optional<ArrayList<?>>, то это означает, что возвращаемая лямбда будет иметь тип ArrayList<?> и поскольку моя лямбда (по крайней мере, как насколько я могу судить), возвращая конкретный тип ArrayList<String>, что-то где-то отбрасывает его постфактум, я думаю?
Да, точно. Разница в том, что тип option является статическим, а возвращаемый тип Optional.map динамически определяется присваиваемой переменной.
Если вы посмотрите на код 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) что-то делает, но да, я полагаю, в этом есть смысл.
См. также этот ответ. В последнем примере кода используется map(Function.identity()), где прямое присвоение Optional невозможно. В контексте этого вопроса A будет ArrayList<?>, а B будет ArrayList<String>.
Ну, первый не работает, потому что дженерики инвариантны, единственный способ сделать их ковариантными — добавить, например, ограниченный тип:
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>...
Да, это компилируется, но меня больше интересует, почему карта это исправляет, это просто упрощенная версия того, что я на самом деле пытаюсь сделать. В моем фактическом коде я переопределяю метод, который должен возвращать эквивалент Optional<ArrayList<?>>, поэтому простое изменение типа этого присваивания на самом деле не помогает.