Java 8将多个可选参数映射到一个函数中

时间:2019-03-05 16:43:29

标签: java java-8 optional

假设我具有函数 Object f(String a, String b) ,并且我想调用两个不同的函数,这些函数返回Optional String以获取f Optional<String> getA() Optional<String> getB() 。我可以想到两个解决方案,但是看起来都不是很干净,尤其是当您有更多参数时:

1:

return getA().flatMap(
    a -> getB().map(
        b -> f(a,b)).get()

2:

Optional<String> a = getA();
Optional<String> b = getB();
if(a.isPresent() && b.isPresent()) {
    return f(a.get(), b.get());
}

是否有一种更清洁的方法?

4 个答案:

答案 0 :(得分:3)

您可以流式传输参数并仅应用一次条件,但是旁观者认为这是否比您的解决方案更优雅:

if (Stream.of(a, b).allMatch(Optional::isPresent)) {
    return f(a.get(), b.get());
}

答案 1 :(得分:3)

您刚刚在函数式编程中偶然发现了一个名为 lifting 的概念,该概念使您可以将常规函数(例如A -> B lift 引入新的域(例如Optional<A> -> Optional<B>)。

在Haskell和类似的语言中,还有一种用于FlatMapping和映射的语法糖更方便地称为 do符号,而在Scala中称为理解。它为您提供了一种使流保持线性并避免嵌套的方法(在示例1中,您不得不这样做)。

不幸的是,Java没有任何种类,因为它的功能编程能力很薄,甚至Optional并不是真正的一等公民(没有标准的API实际使用它)。 因此,您必须坚持已经发现的方法。

如果您对上述概念感到好奇,请继续阅读。

升降

假设您拥有:

public String f(A a, B b) {
    return b + "-" + a;
}

与Scala等效:

def f(a: A, b: B) = b + "-" + a

f提升到Option(与Java中的Optional相同)(使用Scalaz库)看起来像这样:

val lifted = Monad[Option].lift2(f)

lifted现在等效于:

public Optional<String> f(Optional<A> a, Optional<B> b) {
    if(a.isPresent() && b.isPresent()) {
        return Optional.of(b + "-" + a);
    }
    return  Optional.empty;
}

您正在寻找的内容恰好在一行中,并且适用于任何上下文(例如List,而不仅仅是Option)和任何函数。

理解/请勿做

使用进行理解,您的示例如下所示(我认为,我的Scala很弱):

for { 
    a <- getA();
    b <- getB();
} yield f(a, b)

同样,这适用于可以进行平面映射的任何内容,例如ListFuture等。

答案 2 :(得分:1)

我认为,如果没有好的方法来使用Optional,那么就没有理由尝试使用它。

我发现这比您的选择2更干净,更简单

String a = getA().orElse(null);
String b = getB().orElse(null);
if(a != null && b != null) {
    return f(a, b);
}

答案 3 :(得分:0)

如果您确定a和b都存在(如您对解决方案1中对get的最终调用似乎暗示了),我认为这很简单:

    return f(getA().orElseThrow(() -> new NoSuchElementException("a not present")),
             getB().orElseThrow(() -> new NoSuchElementException("b not present")));

如果您不确定两者是否都存在,则最好使用您的解决方案1。它可以最大限度地利用Optional。最后,我不会打电话给get,而是打电话给orElse,或者在您所处的情况下有意义的电话,例如:

    return getA()
            .flatMap(a -> getB().map(b -> f(a,b)))
            .orElse("Not both present");