Java 8 的 Optional.ifPresent 和 if-not-Present 的功能风格?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23773024/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 01:09:32  来源:igfitidea点击:

Functional style of Java 8's Optional.ifPresent and if-not-Present?

javafunctional-programmingjava-8optional

提问by smallufo

In Java 8, I want to do something to an Optionalobject if it is present, and do another thing if it is not present.

在 Java 8 中,Optional如果对象存在,我想对它做一些事情,如果它不存在,则做另一件事。

if (opt.isPresent()) {
  System.out.println("found");
} else {
  System.out.println("Not found");
}

This is not a 'functional style', though.

不过,这不是“功能风格”。

Optionalhas an ifPresent()method, but I am unable to chain an orElse()method.

Optional有一个ifPresent()方法,但我无法链接一个orElse()方法。

Thus, I cannot write:

因此,我不能写:

opt.ifPresent( x -> System.out.println("found " + x))
   .orElse( System.out.println("NOT FOUND"));

In reply to @assylias, I don't think Optional.map()works for the following case:

在回复@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;
});

In this case, when optis present, I update its property and save to the database. When it is not available, I create a new objand save to the database.

在这种情况下,当opt存在时,我更新其属性并保存到数据库。当它不可用时,我创建一个新的obj并保存到数据库中。

Note in the two lambdas I have to return null.

请注意,在我必须返回的两个 lambda 表达式中null

But when optis present, both lambdas will be executed. objwill be updated, and a new object will be saved to the database . This is because of the return nullin the first lambda. And orElseGet()will continue to execute.

但是当opt存在时,两个 lambda 表达式都将被执行。obj将被更新,一个新的对象将被保存到数据库中。这是因为return null在第一个 lambda 中。并且orElseGet()将继续执行。

采纳答案by Bassem Reda Zohdy

For me the answer of @Dane White is OK, first I did not like using Runnable but I could not find any alternatives, here another implementation I preferred more

对我来说,@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;
    }
}

Then :

然后 :

Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s ->System.out.println("isPresent "+s))
            .ifNotPresent(() -> System.out.println("! isPresent"));

Update 1:

更新 1:

the above solution for traditional way of development when you have the value and want to process it but what if I want to define the functionality and the execution will be then, check below enhancement;

当您拥有价值并想要处理它时,传统开发方式的上述解决方案但是如果我想定义功能并且执行将是什么,请检查以下增强功能;

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();
    }
}

Then could be used as:

然后可以用作:

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

In this new code you have 3 things:

在这个新代码中,您有 3 件事:

  1. can define functionality before existing of object easy.
  2. not creating object refrence for each Optional, only one,you have so less memory then less GC.
  3. it is implementing consumer for better usage with other components.
  1. 可以在对象容易存在之前定义功能。
  2. 不为每个 Optional 创建对象引用,只有一个,你的内存比 GC 少。
  3. 它正在实现消费者以更好地与其他组件一起使用。

by the way now its name is more descriptive it is actually Consumer>

顺便说一下,现在它的名字更具描述性,它实际上是消费者>

回答by assylias

An alternative is:

另一种选择是:

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

I don't think it improves readability though.

我不认为它提高了可读性。

Or as Marko suggested, use a ternary operator:

或者按照 Marko 的建议,使用三元运算符:

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

回答by Dane White

There isn't a great way to do it out of the box. If you want to be using your cleaner syntax on a regular basis, then you can create a utility class to help out:

没有一个很好的方法可以开箱即用。如果您想定期使用更简洁的语法,那么您可以创建一个实用程序类来提供帮助:

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);
    }
}

Then you can use a static import elsewhere to get syntax that is close to what you're after:

然后你可以在别处使用静态导入来获得接近你所追求的语法:

import static com.example.OptionalEx.ifPresent;

ifPresent(opt, x -> System.out.println("found " + x))
    .orElse(() -> System.out.println("NOT FOUND"));

回答by Bartosz Bilicki

Java 9 introduces

Java 9 引入

ifPresentOrElse if a value is present, performs the given action with the value, otherwise performs the given empty-based action.

ifPresentOrElse 如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。

See excellent Optional in Java 8 cheat sheet.

请参阅Java 8 中的优秀Optional 备忘单

It provides all answers for most use cases.

它为大多数用例提供了所有答案。

Short summary below

下面的简短摘要

ifPresent() - do something when Optional is set

ifPresent() - 当 Optional 被设置时做一些事情

opt.ifPresent(x -> print(x)); 
opt.ifPresent(this::print);

filter() - reject (filter out) certain Optional values.

filter() - 拒绝(过滤掉)某些可选值。

opt.filter(x -> x.contains("ab")).ifPresent(this::print);

map() - transform value if present

map() - 转换值(如果存在)

opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);

orElse()/orElseGet() - turning empty Optional to default T

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 on empty Optional

orElseThrow() - 在空的 Optional 上懒惰地抛出异常

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

回答by user5057016

Another solution would be to use higher-order functions as follows

另一种解决方案是使用高阶函数如下

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

回答by Alessandro Giusa

Another solution could be following:

另一种解决方案可能如下:

This is how you use it:

这是你如何使用它:

    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"));

Or in case you in case of the opposite use case is true:

或者,如果您遇到相反的用例是真的:

    final Opt<String> opt = Opt.of("This is the text");
    opt.ifNotPresent()
        .apply(() -> System.out.println("Not present"))
        .elseApply(t -> /*do something here*/);

This are the ingredients:

这是成分:

  1. Little modified Function interface, just for the "elseApply" method
  2. Optional enhancement
  3. A little bit of curring :-)
  1. 很少修改Function接口,仅用于“elseApply”方法
  2. 可选增强
  3. 一点点咖喱:-)

The "cosmetically" enhanced Function interface.

“美观”增强的功能界面。

@FunctionalInterface
public interface Fkt<T, R> extends Function<T, R> {

    default R elseApply(final T t) {
        return this.apply(t);
    }

}

And the Optional wrapper class for enhancement:

以及用于增强的可选包装类:

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);
    }
}

This should do the trick and could serve as a basic template how to deal with such requirements.

这应该可以解决问题,并且可以作为如何处理此类需求的基本模板。

The basic idea here is following. In a non functional style programming world you would probably implement a method taking two parameter where the first is a kind of runnable code which should be executed in case the value is available and the other parameter is the runnable code which should be run in case the value is not available. For the sake of better readability, you can use curring to split the function of two parameter in two functions of one parameter each. This is what I basically did here.

这里的基本思想如下。在非函数式编程世界中,您可能会实现一个带有两个参数的方法,其中第一个是一种可运行的代码,在值可用时应执行该代码,另一个参数是可运行的代码,以防万一值不可用。为了更好的可读性,可以使用curring将两个参数的函数拆分为一个参数的两个函数。这就是我在这里所做的基本工作。

Hint: Opt also provides the other use case where you want to execute a piece of code just in case the value is not available. This could be done also via Optional.filter.stuff but I found this much more readable.

提示:Opt 还提供了另一个用例,您希望在该值不可用的情况下执行一段代码。这也可以通过 Optional.filter.stuff 完成,但我发现这更具可读性。

Hope that helps!

希望有帮助!

Good programming :-)

良好的编程:-)

回答by ZhekaKozlov

If you are using Java 9+, you can use ifPresentOrElse()method:

如果您使用的是 Java 9+,则可以使用ifPresentOrElse()方法:

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

回答by Jhutan Debnath

In case you want store the value:

如果您想存储值:

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

回答by Leandro Maro

Supposing that you have a list and avoiding the isPresent()issue (related with optionals) you could use .iterator().hasNext()to check if not present.

假设您有一个列表并避免isPresent()问题(与选项相关),您可以.iterator().hasNext()用来检查是否不存在。

回答by Gerard Bosch

The described behavior can be achieved by using Vavr(formerly known as Javaslang), an object-functional library for Java 8+, that implements most of Scala constructs (being Scala a more expressive language with a way richer type system built on JVM). It is a very good library to add to your Java projects to write pure functional code.

所描述的行为可以通过使用Vavr(以前称为 Javaslang)来实现,Vavr是 Java 8+ 的一个对象函数库,它实现了大多数 Scala 构造(Scala 是一种更具表现力的语言,在 JVM 上构建了更丰富的类型系统)。这是一个非常好的库,可以添加到您的 Java 项目中以编写纯函数式代码。

Vavr provides the Optionmonad that provides functions to work with the Option type such as:

Vavr 提供了Optionmonad,该monad 提供了与 Option 类型一起使用的功能,例如:

  • fold: to map the value of the option on both cases (defined/empty)
  • onEmpty: allows to execute a Runnablewhen option is empty
  • peek: allows to consume the value of the option (when defined).
  • and it is also Serializableon the contrary of Optionalwhich means you can safely use it as method argument and instance member.
  • fold: 在两种情况下映射选项的值(已定义/空)
  • onEmpty: 允许Runnable在选项为空时执行
  • peek: 允许使用选项的值(定义时)。
  • 它也Serializable恰恰相反,Optional这意味着您可以安全地将其用作方法参数和实例成员。

Option follows the monad laws at difference to the Java's Optional "pseudo-monad" and provides a richer API. And of course you can make it from a Java's Optional (and the other way around): Option.ofOptional(javaOptional)–Vavr is focused on interoperability.

Option 遵循与 Java 的 Optional “伪单子”不同的 monad 定律,并提供更丰富的 API。当然,您可以从 Java 的 Optional 中实现(Option.ofOptional(javaOptional)反之亦然):– Vavr 专注于互操作性。

Going to the example:

转到示例:

// AWESOME Vavr functional collections (immutable for the gread good :)
// fully convertible to Java's 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)
);

Further reading

进一步阅读

Null reference, the billion dollar mistake

空引用,十亿美元的错误

N.B.This is only a very little example of what Vavr offers (pattern matching, streams a.k.a. lazy evaluated lists, monadic types, immutable collections,...).

注意这只是 Vavr 提供的一个很小的例子(模式匹配,流又名惰性评估列表,monadic 类型,不可变集合,......)。