Развертывание нескольких необязательных переменных в Java 8
У меня есть следующая проблема. Скажем, у вас есть 2 Optional
variables
Optional<Contact> c1 = ...
Optional<Contact> c2 = ...
и метод, который требует 2 переменных типа Contact
void match(Contact c1, Contact c2) {...}
и вам нужно развернуть оба c1 и c2 Optional
vars и передать их в метод match()
.
Мой вопрос "Какой самый элегантный способ сделать это в Java 8?"
До сих пор я нашел 2 способа:
-
используя isPresent
if (c1.isPresent() && c2.isPresent()) {
match(c1.get(), c2.get());
}
-
используя вложенный ifPresent
c1.ifPresent((Contact _c1) -> {
c2.ifPresent((Contact _c2) -> {
match(_c1, _c2);
});
});
Оба способа ужасны, на мой взгляд. В Scala я могу сделать это:
for {
contact1 <- c1
contact2 <- c2
} yield {
match(contact1, contact2);
}
Есть ли способ в Java 8 сделать это более аккуратным, чем указано выше?
Ответы
Ответ 1
Решение, которое вы предоставили в Scala, является просто синтаксическим сахаром для внутреннего использования flatMaps. Вы также можете использовать flatmaps в Java 8 (но для него нет синтаксического сахара).
c1.flatMap(contact1 -> c2.flatMap(contact2 -> match(contact1, contact2)));
это почти то же самое, что и решение 2, которое вы предоставили. Вы также можете использовать аппликативный функтор из https://github.com/aol/cyclops-react (я один из авторов) или любую другую функциональную библиотеку Java 8.
Аппликативный функтор
Optional<String> o3 = Maybe.fromOptional(o1).ap2(String::concat).ap(o2).toOptional();
Для понимания,
Do.add(o1)
.add(o2)
.yield(a->b->a.concat(b));
Ответ 2
Вы можете отключить понимание Scala для map/flatMap в Java 8 с помощью такой функции:
public static <T,G,V> Optional<V> map2(Optional<T> opt1, Optional<G> opt2, BiFunction<T, G, V> f) {
Optional<V> result = opt1.flatMap(t1 -> opt2.map(t2 -> f.apply(t1, t2)));
return result;
}
А затем передать свою функцию соответствия
Ответ 3
Если вы считаете аргументы, не имеющие значений в качестве исключения, вы можете обрабатывать их, например:
try {
match(c1.orElseThrow(NoVal::new), c2.orElseThrow(NoVal::new));
} catch (NoVal ex) {
...
}
Если они не являются исключительным случаем, я бы пошел с вашим первым вариантом как более конкретным в отношении ваших намерений. На мой взгляд, он довольно понятен и читабельен и легко заменяется на использование orElse
, если вы хотите переключиться на использование значений по умолчанию, если пустые опции.