Функциональный стиль Java 8 Необязательный .ifPresent и if-not-Present?
В Java 8 я хочу что-то сделать с объектом Optional
, если он присутствует, и делать что-то другое, если его нет.
if (opt.isPresent()) {
System.out.println("found");
} else {
System.out.println("Not found");
}
Однако это не "функциональный стиль".
Optional
имеет метод ifPresent()
, но я не могу связать метод orElse()
.
Таким образом, я не могу написать:
opt.ifPresent( x -> System.out.println("found " + x))
.orElse( System.out.println("NOT FOUND"));
В ответ на @assylias я не думаю, что Optional.map()
работает для следующего случая:
opt.map( o -> {
System.out.println("while opt is present...");
o.setProperty(xxx);
dao.update(o);
return null;
}).orElseGet( () -> {
System.out.println("create new obj");
dao.save(new obj);
return null;
});
В этом случае, когда присутствует opt
, я обновляю его свойство и сохраняю его в базе данных. Когда он недоступен, я создаю новый obj
и сохраняю в базе данных.
Обратите внимание, что в двух лямбдах мне нужно вернуть null
.
Но когда присутствует opt
, оба lambdas будут выполнены. obj
будет обновлен, и новый объект будет сохранен в базе данных. Это происходит из-за return null
в первой лямбда. И orElseGet()
будет продолжать выполняться.
Ответы
Ответ 1
Для меня ответ @Dane White в порядке, сначала мне не нравилось использовать Runnable, но я не мог найти альтернатив, здесь другая реализация, которую я предпочел больше
public class OptionalConsumer<T> {
private Optional<T> optional;
private OptionalConsumer(Optional<T> optional) {
this.optional = optional;
}
public static <T> OptionalConsumer<T> of(Optional<T> optional) {
return new OptionalConsumer<>(optional);
}
public OptionalConsumer<T> ifPresent(Consumer<T> c) {
optional.ifPresent(c);
return this;
}
public OptionalConsumer<T> ifNotPresent(Runnable r) {
if (!optional.isPresent()) {
r.run();
}
return this;
}
}
Затем:
Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s ->System.out.println("isPresent "+s))
.ifNotPresent(() -> System.out.println("! isPresent"));
Обновление 1:
вышеупомянутое решение для традиционного способа разработки, когда у вас есть значение и вы хотите его обработать, но что если я захочу определить функциональность и выполнение, тогда отметьте расширение ниже;
public class OptionalConsumer<T> implements Consumer<Optional<T>> {
private final Consumer<T> c;
private final Runnable r;
public OptionalConsumer(Consumer<T> c, Runnable r) {
super();
this.c = c;
this.r = r;
}
public static <T> OptionalConsumer<T> of(Consumer<T> c, Runnable r) {
return new OptionalConsumer(c, r);
}
@Override
public void accept(Optional<T> t) {
if (t.isPresent()) {
c.accept(t.get());
}
else {
r.run();
}
}
Тогда можно использовать как:
Consumer<Optional<Integer>> c=OptionalConsumer.of(System.out::println, ()->{System.out.println("Not fit");});
IntStream.range(0, 100).boxed().map(i->Optional.of(i).filter(j->j%2==0)).forEach(c);
В этом новом коде у вас есть 3 вещи:
- легко определить функциональность до существующего объекта.
- не создавая ссылку на объект для каждого дополнительного, только один, у вас так меньше памяти, чем меньше GC.
- это реализует потребителя для лучшего использования с другими компонентами.
кстати, теперь его название более наглядно, это на самом деле Consumer>
Ответ 2
Если вы используете Java 9+, вы можете использовать метод ifPresentOrElse()
:
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
Ответ 3
См. отличный Необязательный в чит-лист Java 8.
Он предоставляет все ответы для большинства случаев использования.
Краткое описание ниже
ifPresent() - сделать что-то, когда параметр Дополнительно установлен
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
filter() - отклонить (отфильтровать) определенные необязательные значения.
opt.filter(x -> x.contains("ab")).ifPresent(this::print);
map() - значение преобразования, если присутствует
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
orElse()/orElseGet() - поворот пустого Дополнительный по умолчанию T
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
orElseThrow() - lazily throw exceptions на пустом дополнительном
opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
Ответ 4
Альтернативой является
System.out.println(opt.map(o -> "Found")
.orElse("Not found"));
Я не думаю, что это улучшает читаемость.
Или, как предложил Марко, используйте тернарный оператор:
System.out.println(opt.isPresent() ? "Found" : "Not found");
Ответ 5
Другим решением было бы использовать функции более высокого порядка следующим образом
opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
.orElse(() -> System.out.println("Not Found"))
.run();
Ответ 6
Невозможно сделать это из коробки. Если вы хотите регулярно использовать ваш синтаксис, то вы можете создать полезный класс, который поможет:
public class OptionalEx {
private boolean isPresent;
private OptionalEx(boolean isPresent) {
this.isPresent = isPresent;
}
public void orElse(Runnable runner) {
if (!isPresent) {
runner.run();
}
}
public static <T> OptionalEx ifPresent(Optional<T> opt, Consumer<? super T> consumer) {
if (opt.isPresent()) {
consumer.accept(opt.get());
return new OptionalEx(true);
}
return new OptionalEx(false);
}
}
Затем вы можете использовать статический импорт в другом месте, чтобы получить синтаксис, близкий к тому, что вам нужно:
import static com.example.OptionalEx.ifPresent;
ifPresent(opt, x -> System.out.println("found " + x))
.orElse(() -> System.out.println("NOT FOUND"));
Ответ 7
Если вы можете использовать только Java 8 или ниже:
1) если у вас нет spring-data
, лучший способ пока что:
opt.<Runnable>map(param -> () -> System.out.println(param))
.orElse(() -> System.out.println("no-param-specified"))
.run();
Теперь я знаю, что это не так легко читается и даже трудно понять для кого-то, но лично выглядит хорошо для меня, и я не вижу другого хорошего беглого пути для этого случая.
2) если вам повезло и вы можете использовать spring-data
, лучше всего
Optionals # ifPresentOrElse:
Optionals.ifPresentOrElse(opt, System.out::println,
() -> System.out.println("no-param-specified"));
Если вы можете использовать Java 9, вы обязательно должны пойти с:
opt.ifPresentOrElse(System.out::println,
() -> System.out.println("no-param-specified"));
Ответ 8
Описанное поведение может быть достигнуто с помощью Vavr (ранее известного как Javaslang), объектно-функциональной библиотеки для Java 8+, которая реализует большинство конструкций Scala (будучи Scala более выразительным языком с более богатым способом). система типов, построенная на JVM). Это очень хорошая библиотека для добавления в ваши проекты Java для написания чистого функционального кода.
Vavr предоставляет монаду Option
, которая предоставляет функции для работы с типом Option, например:
fold
: отобразить значение параметра в обоих случаях (определено/пусто)
onEmpty
: позволяет выполнить Runnable
, когда опция пуста
peek
: позволяет использовать значение параметра (когда оно определено).
- и это также
Serializable
напротив Optional
, что означает, что вы можете безопасно использовать его в качестве аргумента метода и члена экземпляра.
Option следует монадическим законам, отличным от Java Optional "псевдомонады", и предоставляет более богатый API. И, конечно, вы можете сделать это из Java Optional (и наоборот): Option.ofOptional(javaOptional)
- Vavr ориентирован на взаимодействие.
Переходя к примеру:
// AWESOME Vavr functional collections (immutable for the gread good :)
// fully convertible to Java counterparts.
final Map<String, String> map = Map("key1", "value1", "key2", "value2");
final Option<String> opt = map.get("nonExistentKey"); // you're safe of null refs!
final String result = opt.fold(
() -> "Not found!!!", // Option is None
val -> "Found the value: " + val // Option is Some(val)
);
Дальнейшее чтение
Нулевая ссылка, ошибка в миллиард долларов
N.B. Это всего лишь небольшой пример того, что предлагает Vavr (сопоставление с образцом, потоки и ленивые оценочные списки, монадические типы, неизменяемые коллекции,...).
Ответ 9
Другое решение может быть следующим:
Вот как вы его используете:
final Opt<String> opt = Opt.of("I'm a cool text");
opt.ifPresent()
.apply(s -> System.out.printf("Text is: %s\n", s))
.elseApply(() -> System.out.println("no text available"));
Или в случае, если вы в случае противоположного варианта использования верны:
final Opt<String> opt = Opt.of("This is the text");
opt.ifNotPresent()
.apply(() -> System.out.println("Not present"))
.elseApply(t -> /*do something here*/);
Это ингредиенты:
- Маленький модифицированный функциональный интерфейс, только для метода "elseApply"
- Дополнительное расширение
- Немного о тере: -)
"косметический" расширенный функциональный интерфейс.
@FunctionalInterface
public interface Fkt<T, R> extends Function<T, R> {
default R elseApply(final T t) {
return this.apply(t);
}
}
И дополнительный класс-оболочка для улучшения:
public class Opt<T> {
private final Optional<T> optional;
private Opt(final Optional<T> theOptional) {
this.optional = theOptional;
}
public static <T> Opt<T> of(final T value) {
return new Opt<>(Optional.of(value));
}
public static <T> Opt<T> of(final Optional<T> optional) {
return new Opt<>(optional);
}
public static <T> Opt<T> ofNullable(final T value) {
return new Opt<>(Optional.ofNullable(value));
}
public static <T> Opt<T> empty() {
return new Opt<>(Optional.empty());
}
private final BiFunction<Consumer<T>, Runnable, Void> ifPresent = (present, notPresent) -> {
if (this.optional.isPresent()) {
present.accept(this.optional.get());
} else {
notPresent.run();
}
return null;
};
private final BiFunction<Runnable, Consumer<T>, Void> ifNotPresent = (notPresent, present) -> {
if (!this.optional.isPresent()) {
notPresent.run();
} else {
present.accept(this.optional.get());
}
return null;
};
public Fkt<Consumer<T>, Fkt<Runnable, Void>> ifPresent() {
return Opt.curry(this.ifPresent);
}
public Fkt<Runnable, Fkt<Consumer<T>, Void>> ifNotPresent() {
return Opt.curry(this.ifNotPresent);
}
private static <X, Y, Z> Fkt<X, Fkt<Y, Z>> curry(final BiFunction<X, Y, Z> function) {
return (final X x) -> (final Y y) -> function.apply(x, y);
}
}
Это должно сделать трюк и может служить основным шаблоном для решения таких требований.
Основная идея здесь следующая. В мире программирования без функционального стиля вы, вероятно, реализуете метод, принимающий два параметра, где первый является своего рода исполняемым кодом, который должен выполняться в случае, если значение доступно, а другой параметр - это исполняемый код, который должен выполняться в случае, если значение недоступно. Для лучшей читаемости вы можете использовать curring для разделения функции двух параметров на две функции по одному параметру каждый. Вот что я в основном сделал здесь.
Совет. Опция также предоставляет другой вариант использования, когда вы хотите выполнить часть кода, только если это значение недоступно. Это можно сделать также через файл Optional.filter.stuff, но я нашел, что это гораздо более читаемо.
Надеюсь, что это поможет!
Хорошее программирование: -)
Ответ 10
Если вы хотите сохранить значение:
Pair.of<List<>, List<>> output = opt.map(details -> Pair.of(details.a, details.b))).orElseGet(() -> Pair.of(Collections.emptyList(), Collections.emptyList()));
Ответ 11
Предполагая, что у вас есть список и избегая проблемы isPresent() (связанной с дополнительными функциями), вы можете использовать .iterator(). HasNext(), чтобы проверить, не присутствует ли он.