Я додаю цю другу відповідь на основі запропонованої користувачем 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>>
. Якщо значення було присутнє у введенні Необов’язково, що повертається Необов’язковий містить Потік, який містить єдиний Інший результат. Але якщо значення не було, результат є порожнім Необов’язковим.
Далі, виклик orElseGet(Stream::empty)
повертає значення типу Stream<Other>
. Якщо його вхідне значення присутнє, воно отримує значення, яке є одноелементним Stream<Other>
. В іншому випадку (якщо вхідне значення відсутнє) воно повертається порожнім Stream<Other>
. Отже результат правильний, такий же, як і вихідний умовний код.
У коментарях, що обговорювали мою відповідь, щодо відхиленої редакції, я описав цю методику як "більш стислу, але й більш незрозумілу". Я стою біля цього. Мені знадобилось певний час, щоб зрозуміти, що це робило, і мені також знадобився певний час, щоб написати вищеописаний опис того, що він робив. Ключова тонкість - це перетворення з Optional<Other>
на Optional<Stream<Other>>
. Після того, як ви це зробите, це має сенс, але мені це було очевидно.
Я все-таки визнаю, що спочатку незрозумілі речі можуть з часом стати ідіоматичними. Можливо, ця методика виявляється найкращим способом на практиці, принаймні, поки не Optional.stream
буде додана (якщо вона коли-небудь стане).
ОНОВЛЕННЯ: Optional.stream
додано до JDK 9.
.flatMap(Optional::toStream)
, з вашою версією ви насправді бачите, що відбувається.