it-swarm.com.ru

Функциональный стиль Java 8 Optional.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 присутствует, обе лямбды будут выполнены. obj будет обновлен, и новый объект будет сохранен в базе данных. Это из-за return null в первой лямбде. И orElseGet() будет продолжать выполняться.

195
smallufo

Для меня ответ @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 вещи:

  1. легко определить функциональность до существующего объекта.
  2. не создавая ссылку на объект для каждого дополнительного, только один, у вас так меньше памяти, чем меньше GC.
  3. это реализует потребителя для лучшего использования с другими компонентами.

кстати, теперь его название более наглядно, это на самом деле Consumer>

77
Bassem Reda Zohdy

Если вы используете Java 9, вы можете использовать ifPresentOrElse() method:

opt.ifPresentOrElse(
   value -> System.out.println("Found: " + value),
   () -> System.out.println("Not found")
);
125
ZhekaKozlov

Смотрите отлично Необязательно в шпаргалке Java 8 .

Он предоставляет все ответы для большинства случаев использования.

Краткое резюме ниже

ifPresent () - сделать что-нибудь, когда установлен Optional

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 () - лениво выбрасывать исключения на пустую опцию

opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
67
Bartosz Bilicki

Альтернатива:

System.out.println(opt.map(o -> "Found")
                      .orElse("Not found"));

Я не думаю, что это улучшает читабельность, хотя.

Или, как предложил Марко, используйте троичный оператор:

System.out.println(opt.isPresent() ? "Found" : "Not found");
45
assylias

Другое решение было бы использовать функции высшего порядка следующим образом

opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
   .orElse(() -> System.out.println("Not Found"))
   .run();
32
user5057016

Там нет отличный способ сделать это из коробки. Если вы хотите регулярно использовать более чистый синтаксис, вы можете создать вспомогательный класс, который поможет:

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"));
17
Dane White

Другое решение может быть следующим:

Вот как вы используете это:

    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*/);

Это ингредиенты:

  1. Немного измененный интерфейс Function, только для метода elseApply
  2. Дополнительное улучшение
  3. Немного о карринге :-)

«Косметически» расширенный интерфейс функций.

@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 для разделения функции двух параметров на две функции по одному параметру в каждой. Это то, что я в основном сделал здесь.

Подсказка: Opt также предоставляет другой вариант использования, в котором вы хотите выполнить фрагмент кода на случай, если значение недоступно. Это может быть сделано также через Optional.filter.stuff, но я нашел это гораздо более читабельным.

Надеюсь, это поможет!

Хорошее программирование :-)

2
Alessandro Giusa

Если вы можете использовать только 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"));
1
Tyulpan Tyulpan

Описанное поведение может быть достигнуто с помощью Vavr (ранее известного как Javaslang), объектно-функциональной библиотеки для Java 8+, которая реализует большинство конструкций Scala. Это очень хорошая библиотека для добавления в ваши проекты Java для написания чистого функционального кода.

Vavr предоставляет монаду Option, которая предоставляет функции для работы с типом Option, например:

  • fold: отобразить значение опции в обоих случаях (определено/пусто)
  • onEmpty: позволяет выполнить Runnable, когда опция пуста
  • peek: позволяет использовать значение опции.

Option следует монадическим законам в отличие от опциональной псевдомонады Java и предоставляет более богатый API.

1
Gerard Bosch

Если вы хотите сохранить значение:

Pair.of<List<>, List<>> output = opt.map(details -> Pair.of(details.a, details.b))).orElseGet(() -> Pair.of(Collections.emptyList(), Collections.emptyList()));
0
Jhutan Debnath

Предполагая, что у вас есть список и избегая проблемы isPresent () (связанной с дополнительными функциями), вы можете использовать .iterator (). HasNext (), чтобы проверить, не присутствует ли он.

0
Leandro Maro