Ответ 1
Вы ищете некоторую Monad, но Guava Необязательный (как противоположно, например, Scala Option) - это просто Functor.
Какого черта Functor?!
Functor и Monad - это своего рода коробка, контекст, который обертывает некоторую ценность. Functor, содержащий некоторое значение типа A, знает, как применить функцию A = > B и вернуть результат обратно в Functor. Например: получить что-то из Необязательного, преобразовать и обернуть обратно в Необязательный. В языках функционального программирования такой метод часто называют "map".
Mona.. что?
Monad - почти то же самое, что и Functor, за исключением того, что он потребляет функцию, возвращающую значение, завернутую в Monad (A = > Monad, например Int = > Необязательно). Этот волшебный метод Монады часто называют "flatMap".
Здесь вы можете найти действительно удивительные объяснения для основных терминов FP: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
Фанкорторы и Монады идут!
Необязательно с Java 8 можно классифицировать как Functor (http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#map-java.util.function.Function-) и Monad (<а2 > ).
Nice mon (ad) olog, Marcin, но как я могу решить свою конкретную проблему?
В настоящее время я работаю над проектом, который использует Java 6, и вчера я пишу некоторый вспомогательный класс, называемый "Optionals", который сэкономил мне много времени.
Он предоставляет некоторый вспомогательный метод, который позволяет мне повернуть Необязательный в Monads (flatMap).
Вот код: https://gist.github.com/mkubala/046ae20946411f80ac52
Поскольку моя кодовая база проекта по-прежнему использует nulls как возвращаемое значение, я ввел Optionals.lift(Function), который можно использовать для переноса результатов в Необязательный.
Зачем поднимать результат в Необязательный? Чтобы избежать ситуации, когда функция, переданная в преобразование, может возвращать значение null, а целое выражение возвращает "present of null" (что, кстати, невозможно с Guava Необязательно, из-за этого постусловия → см. Строку # 71 https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Present.java?r=0823847e96b1d082e94f06327cf218e418fe2228#71).
Несколько примеров
Предположим, что findEntity() возвращает необязательный элемент и Entity.getDecimalField(..) может возвращать BigDecimal или null:
Optional<BigDecimal> maybeDecimalValue = Optionals.flatMap(
findEntity(),
new Function<Entity, Optional<BigDecimal>> () {
@Override
public Optional<BigDecimal> apply(Entity input) {
return Optional.fromNullable(input.getDecimalField(..));
}
}
);
Еще один пример, предполагающий, что у меня уже есть функция, которая извлекает десятичные значения из Entities и может возвращать значения null:
Function<Entity, Decimal> extractDecimal = .. // extracts decimal value or null
Optional<BigDecimal> maybeDecimalValue = Optionals.flatMap(
findEntity(),
Optionals.lift(extractDecimal)
);
И последнее, но не менее важное - пример использования:
Optional<Publisher> maybePublisher = Optionals.flatMap(findBook(id), Optionals.lift(Book.getPublisher));
// Assuming that getPublishedBooks may return null..
Optional<List<Book>> maybePublishedBooks = Optionals.flatMap(maybePublisher, Optionals.lift(Publisher.getPublishedBooks));
// ..or simpler, in case when getPublishedBooks never returns null
Optional<List<Book>> maybePublishedBooks2 = maybePublisher.transform(Publisher.getPublishedBooks);
// as a one-liner:
Optionals.flatMap(maybePublisher, Optionals.lift(Publisher.getPublishedBooks)).transform(Publisher.getPublishedBooks);