我目前正在使用 Java 中的 Vert.x,并注意到文档中的示例广泛使用 lambda 作为回调参数。例如:
NetServer server = vertx.createNetServer();
server.listen(1234, "localhost", res -> {
if (res.succeeded()) {
System.out.println("Server is now listening!");
} else {
System.out.println("Failed to bind!");
}
});
查看 listen
的文档函数显示如下:
NetServer listen(int port,
String host,
Handler<AsyncResult<NetServer>> listenHandler)
我的问题是 JVM 如何有机会推断出通用数据类型,例如 Handler<AsyncResult<NetServer>>
出于此类非信息对象,例如 res
?这对于像 JavaScript 这样做 duck-typing 的语言来说似乎很好,但对于像 Java 这样做强类型的语言,这对我来说并不那么明显。如果我们使用匿名类而不是 lambda,所有数据类型都将一目了然。
--编辑-- 正如@Zircon 已经解释的那样,来自 Vert.x 文档的更好示例可能是以下声明:
<T> void executeBlocking(Handler<Future<T>> blockingCodeHandler,
Handler<AsyncResult<T>> resultHandler)
文档中的用法示例:
vertx.executeBlocking(future -> {
// Call some blocking API that takes a significant amount of time to return
String result = someAPI.blockingMethod("hello");
future.complete(result);
}, res -> {
System.out.println("The result is: " + res.result());
});
其中类型不可用,因此只有 Future
上可用的方法和 AsyncResults
可以使用。
最佳答案
编译器以与您完全相同的方式推断类型。
Netserver.listen
需要 Handler<AsyncResult<NetServer>>
作为它的第三个参数。
Handler
是一个有一个方法的 vertx FunctionalInterface handle(E event)
.在这种情况下,E
是AsyncResult<NetServer>
.
在这里插入一个 lambda 可以代替 Handler.handle
.因此,单个参数 res
必须是 AsyncResult<NetServer>
类型.这就是为什么它可以调用 AsyncResult.succeeded
的原因没有问题。
简单地:
listen
的第三个参数是不可能的什么都不是 Handler<AsyncResult<NetServer>>
,因此 lambda 必须提供类型为 <AsyncResult<NetServer>>
的参数.
编辑:
关于在 lambda 中使用嵌套泛型,考虑这个类:
public class myClass<T> {
public void doSomething(int port, String host, Handler<AsyncResult<T>> handler) {
//Stuff happens
}
}
(在这种情况下,我们不关心发生的事情。)
但是,考虑一下我们需要如何调用这个方法。我们需要有一个 MyClass 的实例,这也意味着我们需要在调用 doSomething
之前声明泛型类型。 :
MyClass<String> myObj = new MyClass<String>();
result = myObj.doSomething(port, host, res -> {
if (res.succeeded()) {
System.out.println("I did a thing!");
} else {
System.out.println("I did not do a thing!");
}
});
在这种情况下,编译器仍然可以推断出res
作为AsyncResult<String>
,因为 T
是String
在这种情况下。如果我打开 AsyncResult
, 然后我可以调用 String
类似 toUpperCase
的方法等等。
如果您最终引用了 MyClass<?>
并尝试类似地使用 lambda,res
将被推断为 AsyncResult<?>
. (您可以解包 ?
类型,但由于在编译时无法知道它的类型,您被迫将其视为 Object
。)
如果我们在声明期间不声明泛型类型,我们将收到一条警告,并且由于原始输入,此代码将无法运行(感谢 Holger):
MyClass myObj = new MyClass(); //Generic type warning
result = myObj.doSomething(port, host, res -> {
if (res.succeeded()) { //Error
System.out.println("I did a thing!");
} else {
System.out.println("I did not do a thing!");
}
});
因为我们声明了myObj
作为 MyClass
的原始类型, res
变成类型 Object
(不是 AsyncResult<Object>
),所以我们不能调用 succeeded
在上面。
这样一来,您就不可能在不知道您使用其参数推断的确切类型的情况下使用 lambda。
可能有一些高级方法可以使用 lambda 代替其泛型类型在其自己的签名中声明的方法,但我需要做一些研究来说明这些要点。本质上,即使这可能发生,您也需要调用 MyClass.<MyType>doSomethingStatic
为了在声明 lambda 之前声明类型,以便可以推断类型。
简单地:
您不能在无法推断类型的地方使用 lambda。泛型不会改变这一点。
关于java - Java 8 如何推断 lambdas 参数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45484499/