Java 8 中的链接选项

问题描述 投票:0回答:11

寻找一种链接选项的方法,以便返回第一个存在的选项。 如果不存在

Optional.empty()
应退回。

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

Optional<String> find1()

我正在尝试将它们拴起来:

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

但是这当然行不通,因为

orElse
期望一个值,而
orElseGet
期望一个
Supplier

java lambda java-8 option-type
11个回答
153
投票

使用流:

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

68
投票

受 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是很麻烦的。显然,这个问题将在 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);



52
投票

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

虽然我不确定它会提高可读性(IMO)。 Guava 提供了一种链接Optional的方法:

import com.google.common.base.Optional; Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

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

如果你想保留标准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 方法。


20
投票

读者正在寻找的最有可能的案例(今天)

result = find1() .or(this::find2) .or(this::find3);

Java 8

result = Optional.ofNullable(find1() .orElse(find2() .orElse(find3() .orElse(null))));

性能:上述 
Java 8

解决方案每次都会预调用 find2()find3(),即使(前)find1() 计算返回非空。 更新:具有惰性评估的性能最佳

Java 8

解决方案(感谢@Alexander的评论)将是: result = Optional.ofNullable(find1() .orElseGet(() -> find2() .orElseGet(() -> find3() .orElse(null)))); // <-- null would be the last resort, when all findN are empty. // #ofNullable (in the 1st line would wrap it to Optional // and return Optional.empty() as OP requires.



4
投票


3
投票
orElse

s

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

如果您想要 
orElseThrow

作为结果,请删除

Optional<String>
技巧是将 

findX

返回的每个可选值包装到每个

orElseGet
之前的另一个可选值。
    


2
投票
or

如果提供您需要的数据的函数以纯文本形式返回,您可以这样做:

Optional.ofNullable(supplier1()) .or(() -> Optional.ofNullable(supplier2())) .or(() -> Optional.ofNullable(supplier3())) .orElse(fallbackValue)

我经常静态导入 
ofNullable

以简化可读性。如果需要,您还可以使用

orElse
orElseGet
关闭链条。
现在,如果提供您需要的数据的函数已经返回由 

Optional

包裹的数据,并且您不能或不想更改返回类型,您可以执行此技巧来启动链:

Optional.empty()
  .or(supplier1)
  .or(supplier2)
  .orElse(fallbackValue)



0
投票
对于级联链接您可以使用

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

的价值做某事,您必须将

System.out::println
替换为您的
Consumer
(在此解决方案中也可以有不同的消费者)


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



-2
投票
可选链接

首先转换流到可选使用两种方法之一

findAny() 或 findFirst()
  1. 最小()/最大()
  2. 一旦获得可选,可选还有两个实例方法,它们也存在于 Stream 类中,即 filter 和 map()。 使用这些 on 方法并使用 ifPresent(System.out :: Println) 检查输出

例如:

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

s.findFirst().filter((a)->a+1).ifPresent(System.out::Println)

输出为:2


-3
投票

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
。返回第一个非空的,如果不存在,则返回空的。
    

© www.soinside.com 2019 - 2024. All rights reserved.