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

时间:2014-05-21 02:28:23

标签: java functional-programming java-8 optional

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

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

但这不是一种“功能风格”。

OptionalifPresent()方法,但我无法链接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并保存到数据库。

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

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

12 个答案:

答案 0 :(得分:147)

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

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

答案 1 :(得分:88)

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

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

  1. 可以在对象容易存在之前定义功能。
  2. 没有为每个Optional创建对象引用,只有一个,你的内存少于GC。
  3. 正在实施消费者以更好地使用其他组件。
  4. 顺便说一句,现在它的名称更具描述性,实际上是消费者&gt;

答案 2 :(得分:69)

查看优秀的Optional in Java 8 cheat sheet

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

以下简短摘要

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

答案 3 :(得分:46)

另一种选择是:

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

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

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

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

答案 4 :(得分:33)

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

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

答案 5 :(得分:18)

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

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

答案 6 :(得分:2)

另一个解决方案可能是:

这是您使用它的方式:

    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. 很少修改的功能界面,仅适用于“elseApply”方法
  2. 可选增强
  3. 一点点曲线: - )
  4. “美容”增强功能界面。

    @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完成,但我发现它更具可读性。

    希望有所帮助!

    良好的编程: - )

答案 7 :(得分:2)

如果只能使用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"));

答案 8 :(得分:1)

可以通过使用Vavr(以前称为Javaslang)来实现所描述的行为,Null reference, the billion dollar mistake是Java 8+的对象功能库,它实现了大多数Scala构造(成为Scala一种更具表达性的语言,具有更丰富的方式)基于JVM的类型系统)。这是添加到Java项目中以编写纯功能代码的很好的库。

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

  • fold:在两种情况下(已定义/空)映射选项的值
  • onEmpty:允许在选项为空时执行Runnable
  • peek:允许使用该选项的值(定义时)。

Option遵循monad法则,与Java的“ pseudo-monad”法不同,它提供了更丰富的API。当然,您可以从Java的Optional(以及相反的方法)中实现:Option.ofOptional(javaOptional) –Vavr专注于互操作性。

转到示例:

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

进一步阅读

electron's screen object

N.B。这只是Vavr提供的功能的一个很小的例子(模式匹配,又名流懒惰求值列表,单子类型,不可变集合等)。

答案 9 :(得分:1)

您不能在orElse之后调用ifPresent,原因是,orElse是在一个优化函数上调用的,但是ifPresent返回void。因此,最好的实现方法是ifPresentOrElse。 可能是这样的:

op.ifPresentOrElse( 
            (value) 
                -> { System.out.println( 
                         "Value is present, its: "
                         + value); }, 
            () 
                -> { System.out.println( 
                         "Value is empty"); }); 

答案 10 :(得分:0)

如果要存储值:

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

答案 11 :(得分:0)

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