Java 8的Optional.ifPresent和if-not-Present的函数风格?

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

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

不过,这不是一种“function性风格”。

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出现时,我更新它的属性并保存到数据库中。 当它不可用时,我创build一个新的obj并保存到数据库。

注意在两个lambda中,我必须返回null

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

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

上面的解决scheme对于传统的开发方式,当你有价值并想要处理它,但是如果我想定义function和执行的话会怎么样呢,请查看下面的增强;

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

在这个新代码中你有三件事情:

  1. 可以在对象的存在容易之前定义function。
  2. 没有创build对每个可选的对象引用,只有一个,你有更less的内存,然后lessGC。
  3. 它正在实现消费者与其他组件更好地使用。

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

在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() – 在空可选的情况下延迟抛出exception

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

另一种方法是:

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

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

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

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

另一个解决scheme是使用如下的高阶函数

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

如果您正在使用Java 9,则可以使用ifPresentOrElse()方法:

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

没有很好的方法来开箱。 如果你想定期使用清晰的语法,那么你可以创build一个工具类来帮助你:

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

另一个解决scheme可能如下:

这是你如何使用它:

  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. 修改了很less的函数接口,仅用于“elseApply”方法
  2. 可选的增强
  3. 一点点curring 🙂

“美观”增强function界面。

 @FunctionalInterface public interface EFunction<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 EFunction<Consumer<T>, EFunction<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> EFunction<X, EFunction<Y, Z>> curry(final BiFunction<X, Y, Z> function) { return (final X x) -> (final Y y) -> function.apply(x, y); } } 

这应该做的伎俩,可以作为如何处理这些要求的基本模板。

这里的基本思想如下。 在非function风格的编程世界中,您可能会实现一个方法,它带有两个参数,其中第一个是可运行的代码,如果该值可用,则应该执行该代码,而另一个参数是可运行的代码,值不可用。 为了提高可读性,可以使用Curring将两个参数的function分别分为一个参数的两个function。 这是我在这里基本做的。

提示:Opt还提供了另外一个用例,在这个用例中你要执行一段代码,以防万一这个值不可用。 这也可以通过Optional.filter.stuff来完成,但是我发现这更可读。

希望有所帮助!

好编程:-)