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

sma*_*ufo 239 java functional-programming optional java-8

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

if (opt.isPresent()) {
  System.out.println("found");
} else {
  System.out.println("Not found");
}
Run Code Online (Sandbox Code Playgroud)

但这不是一种"功能风格".

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

因此,我不能写:

opt.ifPresent( x -> System.out.println("found " + x))
   .orElse( System.out.println("NOT FOUND"));
Run Code Online (Sandbox Code Playgroud)

在回复@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;
});
Run Code Online (Sandbox Code Playgroud)

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

请注意我必须返回的两个lambda null.

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

Zhe*_*lov 180

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

opt.ifPresentOrElse(
   value -> System.out.println("Found: " + value),
   () -> System.out.println("Not found")
);
Run Code Online (Sandbox Code Playgroud)

  • 像这样的两个 lambda 相当难看。我认为 if/else 对于这些情况来说要干净得多。 (5认同)
  • @john16384 好吧,如果你觉得它很难看,那么我会删除我的答案(不)。 (4认同)
  • 很好,因为它几乎和Scala中的模式匹配一​​样干净 (3认同)
  • 这非常好,但这个问题是专门针对 JDK8 的,因为 ifPresentOrElse 不可用。 (2认同)

Bas*_*hdy 101

对我来说@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后 :

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

更新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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以用作:

    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);
Run Code Online (Sandbox Code Playgroud)

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

  1. 可以在对象存在之前定义功能.
  2. 不为每个Optional创建对象引用,只有一个,你的内存少于GC.
  3. 它正在实现消费者以更好地使用其他组件.

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

  • 应该使用Optional.ofNullable(o)而不是Optional.of(o) (2认同)
  • 如果您不确定要使用的值是否为 null 并且不需要面对 NPE,则需要使用 ofNullable,如果您确定它不为 null 或者您不在乎是否获得NPE。 (2认同)

Bar*_*cki 74

请参阅Java 8备忘单中的优秀可选项.

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

以下简短摘要

ifPresent() - 设置Optional时执行某些操作

opt.ifPresent(x -> print(x)); 
opt.ifPresent(this::print);
Run Code Online (Sandbox Code Playgroud)

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

opt.filter(x -> x.contains("ab")).ifPresent(this::print);
Run Code Online (Sandbox Code Playgroud)

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

opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
Run Code Online (Sandbox Code Playgroud)

orElse()/ orElseGet() - 变为空可选为默认T.

int len = opt.map(String::length).orElse(-1);
int len = opt.
    map(String::length).
    orElseGet(() -> slowDefault());     //orElseGet(this::slowDefault)
Run Code Online (Sandbox Code Playgroud)

orElseThrow() - 懒惰地抛出空可选的异常

opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);
Run Code Online (Sandbox Code Playgroud)

  • 这实际上并没有回答OP的问题.它回答了许多常见的用途,但不是OP所要求的. (57认同)
  • @Matt否,OP特别要求在可选项不存在时执行的操作,不在它是否存在时返回值.OP甚至在使用orElseGet的问题中提到了类似的东西,解释了为什么它不起作用. (4认同)
  • @CaptainMan我明白你的意思了.如果他没有从`map`返回null,我认为他可以使它工作,但是要求一个功能解决方案让你可以调用DAO有点奇怪.在我看来,从这个`map.orElse`块返回更新的/ new对象更有意义,然后做你需要对返回的对象做的事情. (2认同)

ass*_*ias 50

另一种选择是:

System.out.println(opt.map(o -> "Found")
                      .orElse("Not found"));
Run Code Online (Sandbox Code Playgroud)

我不认为这会提高可读性.

或者正如Marko建议的那样,使用三元运算符:

System.out.println(opt.isPresent() ? "Found" : "Not found");
Run Code Online (Sandbox Code Playgroud)

  • 谢谢 @assylias ,但我认为 Optional.map() 不适用于这种情况(请参阅我的上下文更新)。 (2认同)
  • @smallufo你需要在你的第一个lambda中返回`new Object();`但是老实说这变得非常难看.我会坚持使用if/else来更新你的例子. (2认同)

小智 37

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

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

  • 在我看来,迄今为止没有JDK 9的最佳解决方案. (8认同)
  • 解释会很棒.我问自己,为什么你需要使用一个runnable map(?)以及`value - >() - > syso`部分的含义. (5认同)

Dan*_*ite 20

没有很好的方法可以开箱即用.如果您希望定期使用更清晰的语法,那么您可以创建一个实用程序类来帮助:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

import static com.example.OptionalEx.ifPresent;

ifPresent(opt, x -> System.out.println("found " + x))
    .orElse(() -> System.out.println("NOT FOUND"));
Run Code Online (Sandbox Code Playgroud)

  • `Optional.ifPresentOrElse()`已被添加到JDK 9中. (35认同)

Tyu*_*pan 7

如果只能使用Java 8或更低版本:

1)如果spring-data到目前为止您没有最好的方法:

opt.<Runnable>map(param -> () -> System.out.println(param))
      .orElse(() -> System.out.println("no-param-specified"))
      .run();
Run Code Online (Sandbox Code Playgroud)

现在我知道它对某人不是那么可读,甚至很难理解,但对我个人而言看起来不错,而且我认为这种情况下没有另一种流畅的方式。

2)如果您足够幸运,可以使用spring-data最好的方法是 Optionals#ifPresentOrElse

Optionals.ifPresentOrElse(opt, System.out::println,
      () -> System.out.println("no-param-specified"));
Run Code Online (Sandbox Code Playgroud)

如果可以使用Java 9,则绝对应该使用:

opt.ifPresentOrElse(System.out::println,
      () -> System.out.println("no-param-specified"));
Run Code Online (Sandbox Code Playgroud)