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);
在这个新代码中你有三件事情:
- 可以在对象的存在容易之前定义function。
- 没有创build对每个可选的对象引用,只有一个,你有更less的内存,然后lessGC。
- 它正在实现消费者与其他组件更好地使用。
顺便说一下,它的名字是更具描述性的,它实际上是消费者>
在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*/);
这是成分:
- 修改了很less的函数接口,仅用于“elseApply”方法
- 可选的增强
- 一点点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来完成,但是我发现这更可读。
希望有所帮助!
好编程:-)