Я добавляю этот второй ответ на основе предложенного редактирования пользователем srborlongan к моему другому ответу . Я думаю, что предложенная техника была интересной, но она не очень подходила для редактирования моего ответа. Другие согласились, и предложенное редактирование было отклонено. (Я не был одним из избирателей.) Однако у техники есть свои достоинства. Было бы лучше, если бы srborlongan разместил свой ответ. Этого еще не произошло, и я не хотел, чтобы техника терялась в тумане отклоненной истории редактирования StackOverflow, поэтому я решил представить ее как отдельный ответ.
В основном, техника заключается Optional
в умном использовании некоторых методов, чтобы избежать необходимости использования троичного оператора ( ? :
) или оператора if / else.
Мой встроенный пример будет переписан так:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Мой пример, который использует вспомогательный метод, будет переписан следующим образом:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
return opt.map(Stream::of)
.orElseGet(Stream::empty);
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
КОММЕНТАРИЙ
Давайте сравним оригинальную и модифицированную версии напрямую:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
Оригинал - простой, если рабочий подход: мы получаем Optional<Other>
; если оно имеет значение, мы возвращаем поток, содержащий это значение, а если оно не имеет значения, мы возвращаем пустой поток. Довольно просто и легко объяснить.
Модификация умна и имеет то преимущество, что избегает условных выражений. (Я знаю, что некоторым людям не нравится троичный оператор. При неправильном использовании он действительно может затруднить понимание кода.) Однако иногда вещи могут быть слишком умными. Измененный код также начинается с Optional<Other>
. Затем он вызывает, Optional.map
который определяется следующим образом:
Если значение присутствует, примените к нему предоставленную функцию сопоставления, а если результат не равен нулю, верните необязательный параметр, описывающий результат. В противном случае вернуть пустой необязательный.
map(Stream::of)
Вызов возвращает Optional<Stream<Other>>
. Если значение присутствовало во входном необязательном элементе, возвращаемый необязательный элемент содержит поток, содержащий единственный результат Other. Но если значение не присутствовало, результатом является пустой Необязательный.
Далее вызов to orElseGet(Stream::empty)
возвращает значение типа Stream<Other>
. Если его входное значение присутствует, оно получает значение, которое является единственным элементом Stream<Other>
. В противном случае (если входное значение отсутствует) возвращается пустое значение Stream<Other>
. Таким образом, результат правильный, такой же, как исходный условный код.
В комментариях, обсуждающих мой ответ относительно отклоненного редактирования, я описал эту технику как «более краткую, но и более неясную». Я поддерживаю это. Мне потребовалось некоторое время, чтобы понять, что он делает, и мне также понадобилось некоторое время, чтобы написать приведенное выше описание того, что он делал. Ключевой тонкостью является преобразование из Optional<Other>
в Optional<Stream<Other>>
. Как только вы поймете это, это имеет смысл, но это не было очевидно для меня.
Я признаю, однако, что вещи, которые изначально неясны, могут со временем стать идиоматическими. Может случиться так, что эта техника окажется лучшим на практике, по крайней мере, до тех пор, пока не Optional.stream
будет добавлена (если она вообще будет).
ОБНОВЛЕНИЕ: Optional.stream
было добавлено в JDK 9.
.flatMap(Optional::toStream)
, с вашей версией, вы на самом деле видите, что происходит.