寻找一种链接选项的方法,以便返回第一个存在的选项。如果不存在,则应返回Optional.empty()
。
假设我有几种这样的方法:
Optional<String> find1()
我试图将它们联系起来:
Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );
但当然这不起作用,因为orElse
需要一个值而orElseGet
需要一个Supplier
。
答案 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)
执行可选链接首先转换 流到可选使用这两种方法之一
获得可选项后,可选的还有两个实例方法,它们也存在于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过滤。返回第一个非空的,或者如果不存在,则返回空的。