如果这是 FP 中的基本问题,请原谅我。让我们考虑可选单子。我知道我可以使用
map
根据函数将 Optional
转换为另一个 Optional
(如果可选值有值,将调用该函数)。但是如果我的函数有两个输入而不是一个怎么办?
例如:
var data = Optional.of(100);
var result = data.map(x -> x * 2); //all good!
但是呢:
int getData(Connection conn, int data) { ... }
var data = Optional.of(100);
var connection = getOptionalDataConnection();
var result = data.map(i -> getData(connection?, i)); //this does not work as the getData function only accepts non-optionals
我确信 FP 世界中有一个术语,我只是不熟悉它。但我的想法是我有一个地图函数,它接受“两个”输入,而不是一个。我想要一个助手/实用程序/...如果两个输入都在那里,它将调用我的函数。但如果其中任何一个是“空”,则应返回
Optional.empty()
。
这叫什么?Java 支持吗?
在我们深入研究这个问题之前,我的蜘蛛侠感官已经开始刺痛了。这个:
但是如果其中任何一个是“空”,则应返回Optional.empty()。
这是一个坏主意。如果你替换 50 个 NullPointerException 错误,这在客观上是鲁棒性和生产力的一个相当大的损失,这至少直接指出了问题所在并确保执行不会继续,用 10 个'呵呵,奇怪 - 这段代码......它......做了由于某种原因没有任何错误。这 10 个错误可能会产生非常严重的副作用(因为代码会继续),并且需要花费比这些 NPE 多 5 倍的时间来修复,因此您的处境会更糟,需要花费更多的时间来寻找错误。而这种随意的违约(“呃,什么也不做!”)正是你到达那个令人讨厌的地方的方式。 所以,你知道的——对SO的品味进行深入研究可能有点太过分了,但是,哦。不要做任何这样的事情。我不太确定这总体上是个好主意,但是
在java中这不好。 注意:因为泛型和可选是不好的,所以我用
int
替换了
Integer
的所有使用,因为由于基元和泛型的限制,如果不这样做,这将无法正常工作。部分申请
您当前拥有的是一个接受
Connection
和
Integer
并返回 Integer
的函数。您需要的是一个接受 Optional<Connection>
、Integer
并返回 Optional<Integer>
的函数。具体来说,提供了 Optional<Connection>
,但 Integer 是输入。那么让我们这样做吧:Optional<Connection> connection = getOptionalDataConnection();
Function<Integer, Optional<Integer>> f = i -> connection.map(c -> Optional.of(getData(c, i)));
有了这个功能,我们就可以轻松完成工作了:
Optional<Connection> connection = getOptionalDataConnection();
Function<Integer, Optional<Integer>> f = i -> connection.map(c -> Optional.of(getData(c, i)));
Optional<Integer> data = Optional.of(100);
var result = data.flatMap(i -> f.apply(i));
这是相当多的代码,对于我的敏感性来说,这里大部分都失去了真正的本质。但是,这就是 java 中的内容(具体来说,Optional 的
flatMap
)。您可以编写实用程序方法,将选项转换为可平面映射的函数。
我们把它放在一起:import java.util.function.*;
import java.util.*;
class Example {
public static <U, V, R> Optional<R> compose(
Optional<U> first, Optional<V> second,
BiFunction<U, V, R> func) {
Function<V, Optional<R>> f = v -> first.map(u -> func.apply(u, v));
return second.flatMap(v -> f.apply(v));
}
public static String make(String a, Integer b) {
return a + ", " + b;
}
public static void main(String[] args) {
Optional<String> as = Optional.of("Hello");
Optional<Integer> bs = Optional.of(100);
Optional<String> an = Optional.empty();
Optional<Integer> bn = Optional.empty();
BiFunction<String, Integer, String> func = Example::make;
System.out.println(compose(as, bs, func));
System.out.println(compose(as, bn, func));
System.out.println(compose(an, bs, func));
System.out.println(compose(an, bn, func));
}
}
与本例有些相关的相关术语是“柯里化”。将各种内容包装在可选中的更一般原则,我什至不确定它是否有名称。