寻找一种链接可选项的方法,以便返回存在的第一个。如果不存在,Optional.empty()
应返回。
假设我有几种这样的方法:
Optional<String> find1()
我正在尝试将它们链接起来:
Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );
但是当然这是行不通的,因为orElse
期望一个值并且orElseGet
期望一个Supplier
。
Answers:
使用流:
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();
map
可以更改为.map(Supplier::get)
,第二个可以更改为.map(Optional::get)
。当您使用它时,.filter(Optional::isPresent)
也可以,但是不会像其他方法那样损害可读性。
Stream.of(opt1,opt2,opt3).flatMap(Optional::stream).findFirst()
。
您可以这样做:
Optional<String> resultOpt = Optional.of(find1()
.orElseGet(() -> find2()
.orElseGet(() -> find3()
.orElseThrow(() -> new WhatEverException()))));
尽管我不确定它是否可以提高IMO的可读性。番石榴提供了一种链接可选项的方法:
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方法,就像已经提到的其他方法一样。
or(Optional<T>... opts)
会有所改善。但是,带有泛型的Varargs是不幸的混合。
find1
或find2
将返回一个值。
find1
返回一个值(= non-empty Optional
),则返回值find3
无所谓。但是,尽管我们已经有了一个有效的结果,但是在您的第一个解决方案中可能会引发异常。换句话说:理想情况下find3
不应该首先调用它。
受到索利(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的,Optional::or
但它是在Java 9中引入的。使用它,可以按以下方式解决问题
Optional<String> result = find1()
.or(this::find2)
.or(this::find3);
级联链接,您可以使用 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
(此解决方案中也可以使用不同的消费者)
执行可选链接首先使用两种方法之一将 流转换为可选
一旦获得了可选参数,可选参数还有两个实例方法,它们也存在于Stream类中,即filter和map()。使用这些on方法并检查输出是否使用ifPresent(System.out :: Println)
例如:
流s = Stream.of(1,2,3,4);
s.findFirst()。filter((a)-> a + 1).ifPresent(System.out :: Println)
输出为:2
Stream<Integer> s = Stream.of(null,2,3,4); s.findFirst().map((a)->a+1).ifPresent(System.out::println);
也许是其中之一
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。返回第一个非空的,如果不存在,则返回一个空的。
Supplier
会.orElseGet()
。