flatMap 和 map 是常见的 Scala(或更一般地,函数式编程)函数。它们都接受 function 作为参数。为了将 Play WS API 翻译成 Java(以及几乎所有其他内容),Scala 的函数类型需要在 Java 中重新实现,以便您可以充分利用 WS 库。它在这里以与 Scala 编译器类似的方式完成。 Function<A,B> 是一种抽象类型,需要 apply 方法。 apply的参数是函数的参数,apply的返回类型是函数的类型。
如果你在 Java 中有这个功能:
public String int2String(Integer integer) {
return integer.toString();
}
相当于这样:
new Function<Integer, String>() {
public String apply(Integer integer) {
return integer.toString();
}
}
让我们从只有一个 WS 调用的情况开始。 WS.url(...).get() 返回 Promise<WS.Response>。 Promise 是 promised 值的容器类。为了处理它包含(或最终将包含)的值,我们需要使用map 函数。对于Promise<WS.Response>,map 将接受Function<WS.Response, T> 作为参数,其中T 是您要将响应映射到的类型。
例如,让我们定义一个 Function,它只会在 Play HTTP Result 中返回 WS.Response 的正文:
Function<WS.Response, Result> echo = new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
现在让我们在控制器的 WS 调用中使用这个 Function:
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(echo);
return resultPromise;
}
一旦满足Promise,前面定义的echo函数将在map内部执行,返回Promise<Result>。前面两段代码也可以这样写(与匿名函数合二为一):
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
);
return resultPromise;
}
作为一个粗略的例子,假设我们需要进行两次 WS 调用。第二个 WS 调用将取决于第一个。也许第一次调用会给我们一些 URL,我们将使用它来进行第二次 WS 调用。
这就是flatMap 出现的地方。我们需要两个函数来完成这个任务。第一个函数是传递给flatMap 的函数,它将在收到第一个WS.Response 时执行。第一个函数将使用第一个响应来进行第二个 WS 调用,该调用返回另一个 Promise<WS.Response>,必须映射它才能获得最终结果。所以我们 map 使用第二个函数将第二个结果转换为 WS.Response 到我们的 Result。
那么发生了什么?如果我们在这两种情况下都使用map 而不是flatMap,则事件链将如下所示:
第一个get()返回一个Promise<WS.Response>,然后我们将map包含的WS.Response返回一个Promise<Result>。然而,这会给我们留下一个Promise<Promise<WS.Response>>,这不是很理想。在外部函数中使用flatMap 会将Promises 扁平化为单个Promise<Result>。同样,如果您进行 3 次或更多嵌套调用,您会将每个结果 map 传递给一个内部函数,并且在外部级别只有一个 flatMap 以在最后展平所有内容。
这一切在 Scala 中当然看起来更漂亮。