简体   繁体   English

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

[英]Java 8 Mapping multiple optional parameters into a function

Let's say that I have function Object f(String a, String b) and I want to call two different functions that return Optional Strings to get the parameters for f Optional<String> getA() and Optional<String> getB() . 假设我有函数Object f(String a, String b) ,我想调用两个返回Optional Strings的不同函数来获取f Optional<String> getA()Optional<String> getB() I can think of two solutions but neither look all that clean, especially when you have even more parameters: 我可以想到两个解决方案,但看起来都不干净,特别是当你有更多参数时:

1: 1:

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

2: 2:

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

Is there a cleaner way to do this? 有更清洁的方法吗?

You could stream the arguments and apply the condition only once, but whether or not this is more elegant than your solutions is in the eye of the beholder: 您可以流式传输参数并仅应用条件一次,但是这是否比您的解决方案更优雅在旁观者眼中:

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

You've just stumbled upon a concept called lifting in functional programming, that enables you to lift regular functions (eg A -> B ) into new domains (eg Optional<A> -> Optional<B> ). 您刚刚偶然发现了一个名为提升函数式编程的概念,它使您能够常规函数(例如A -> B提升到新域(例如Optional<A> -> Optional<B> )。

There's also a syntactic sugar for flatMapping and mapping more comfortably called the do notation in Haskell and similar languages, and for comprehension in Scala. 还有一个用于flatMapping的语法糖和更舒适的映射,称为Haskell和类似语言中的符号 ,以及Scala中的理解 It gives you a way to keep the flow linear and avoid nesting (that you were forced to go through in your example 1). 它为您提供了一种保持流线性并避免嵌套的方法(您在示例1中被迫通过)。

Java, unfortunately has nothing of the sort, as its functional programming capabilities are meager, and even Optional isn't really a first-class citizen (no standard API actually uses it). 遗憾的是,Java没有任何类型,因为它的函数式编程功能微不足道,甚至Optional也不是真正的一等公民(没有标准API实际使用它)。 So you're stuck with the approaches you've already discovered. 所以你坚持使用你已经发现的方法。

In case you're curious about the concepts mentioned above, read on. 如果您对上述概念感到好奇,请继续阅读。

Lifting 吊装

Assuming you have: 假设你有:

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

With its Scala equivalent: 使用Scala等效:

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

Lifting f into Option (same as Optional in Java) would look like this (using Scalaz library): f提升为Option (与Java中的Optional相同)将如下所示(使用Scalaz库):

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

lifted is now a function equivalent to: lifted现在的功能相当于:

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

Exactly what you're looking for, in 1 line, and works for any context (eg List , not just Option ) and any function. 正是您正在寻找的,在一行,并适用于任何上下文(例如List ,而不仅仅是Option )和任何功能。

For comprehension / Do notation 为了理解/做符号

Using for comprehension , your example would look like this (I think , my Scala is weak): 用于理解 ,你的例子看起来像这样(我 ,我的Scala很弱):

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

And again, this is applicable to anything that can be flatMapped over, like List , Future etc. 而且,这适用于任何可以平面映射的内容,如ListFuture等。

I'm of the opinion that if there is no good way to use Optional , then there is no reason to try to use it anyway. 我认为如果没有好的方法可以使用Optional ,那么无论如何都没有理由尝试使用它。

I find this to be cleaner and simpler than your option 2: 我觉得这比你的选择2更清洁,更简单:

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

If you are sure that a and b are both present (as your final call to get in solution 1 seems to suggest), I think it is pretty straightforward: 如果您确定a和b都存在(因为您最终要求get解决方案1似乎建议),我认为这非常简单:

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

If you aren't sure that both are present, I would prefer your solution 1. It exploits Optional the best. 如果你不知道这两个都存在,我会1.它利用更喜欢你的解决方案Optional最好的。 Only I would not call get at the end, but rather orElse or what makes sense in your situation, for example: 只有我不会在最后调用get ,而是在你的情况下orElse或者是有意义的,例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM