我目前正在使用Java中的Vert.x,并注意到文档中的示例广泛使用lambdas作为回调参数。例如:
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这样的语言来说这似乎很好,但是对于像Java那样做强打字的语言,它对我来说并不那么明显。如果我们使用匿名类而不是lambda,那么所有数据类型都将在板上。
- EDIT-- 正如@Zircon已经解释的那样,Vert.x文档中可能更好的例子是声明:
<T> void executeBlocking(Handler<Future<T>> blockingCodeHandler,
Handler<AsyncResult<T>> resultHandler)
以及来自docs的用法示例:
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
上可用的方法。
答案 0 :(得分:5)
编译器以与您完全相同的方式推断出类型。
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。泛型不会改变这一点。