Java 8中的链接选项

时间:2015-02-14 10:32:48

标签: java lambda java-8 optional

寻找一种链接选项的方法,以便返回第一个存在的选项。如果不存在,则应返回Optional.empty()

假设我有几种这样的方法:

Optional<String> find1()

我试图将它们联系起来:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );

但当然这不起作用,因为orElse需要一个值而orElseGet需要一个Supplier

9 个答案:

答案 0 :(得分:74)

使用流:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

如果您需要懒惰地评估查找方法,请使用供应商功能:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

答案 1 :(得分:30)

你可以这样做:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));

虽然我不确定它是否提高了IMO的可读性。 Guava提供了一种链接Optionals的方法:

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

它可能是您的问题的另一种选择,但不使用JDK中的标准Optional类。

如果您想保留标准API,可以编写一个简单的实用程序方法:

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}

然后:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));

如果你有许多连锁店的选项,也许最好使用Stream方法,就像其他提到的一样。

答案 2 :(得分:20)

受Sauli的回答启发,可以使用flatMap()方法。

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();

将Optional转换为Stream非常麻烦。显然,这将是fixed with JDK9。所以这可以写成

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();

Java 9发布后的更新

虽然最初的问题是关于Java 8,但是在Java 9中引入了Optional::or。有了它,问题可以解决如下

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);

答案 3 :(得分:1)

{{1}}

答案 4 :(得分:0)

基于Alexis C的回答,但没有orElse的嵌套

String result = find1()
                   .map(Optional::of)
                   .orElseGet(Foo::find2())
                   .map(Optional::of)
                   .orElseGet(Foo::find3())
                   .orElseThrow(() -> new WhatEverException())

如果您希望将orElseThrow作为结果,请删除Optional<String>

诀窍是将findX返回的每个可选内容包装到每个orElseGet之前的另一个可选内容。

答案 5 :(得分:0)

用于级联链接 passwordObject

let data = passwordObject && passwordObject.value ? await bcrypt.compare(password, passwordObject.value) : false;

要使用ifPresentOrElse的值执行某项操作,您必须用find1().ifPresentOrElse( System.out::println, new Runnable() { public void run() { find2().ifPresentOrElse( System.out::println, new Runnable() { public void run() { find3().ifPresentOrElse( System.out::println, new Runnable() { public void run() { System.err.println( "nothing found…" ); } } ); } } ); } } ); 替换Optional
(在此解决方案中也可以使用不同的消费者)

答案 6 :(得分:0)

我针对所有这些问题的通用通用解决方案:

public static <T> T firstMatch(final Predicate<T> matcher, final T orElse, final T... values) {
  for (T t : values) {
    if (matcher.test(t)) {
      return t;
    }
  }
  return orElse;
}

然后你可以这样做:

public static <T> Optional<T> firstPresent(final Optional<T>... values) {
  return firstMatch(Optional::isPresent, Optional.empty(), values);
}

答案 7 :(得分:-1)

执行可选链接首先转换 流到可选使用这两种方法之一

  1. findAny()或findFirst()
  2. min()/ max()
  3. 获得可选项后,可选的还有两个实例方法,它们也存在于Stream类中,即filter和map()。 在方法上使用这些并检查输出使用ifPresent(System.out :: Println)

    例如:

    Stream s = Stream.of(1,2,3,4);

    s.findFirst()。filter((a) - &gt; a + 1).ifPresent(System.out :: Println)

    输出为:2

答案 8 :(得分:-4)

可能是其中一个

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
        if (first.isPresent()) return first;
        for (Supplier<Optional <? extends T>> sup : supp) {
            Optional<? extends T> opt = sup.get();
            if (opt.isPresent()) {
                return opt;
            }
        }
        return Optional.empty();
    }

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
        if (first.isPresent()) return first;
        Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
        return present.findFirst().orElseGet(Optional::empty);
    }

会做的。

第一个迭代一系列供应商。返回第一个非空Optional<>。如果我们找不到,我们会返回一个空的Optional

第二个对Stream遍历的Suppliers执行相同的操作,每个人(懒惰地)询问它们的值,然后对其进行空Optional s过滤。返回第一个非空的,或者如果不存在,则返回空的。