Java8如何推断lambdas参数类型
我目前正在Java中使用Vert.x,并注意到文档中的示例广泛使用lambda作为回调参数。例如:Java8如何推断lambdas参数类型,java,lambda,java-8,vert.x,Java,Lambda,Java 8,Vert.x,我目前正在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(
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)
以文档中的用法为例:
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
将处理程序
作为其第三个参数
处理程序
是一个vertx功能接口,具有一个方法处理(E事件)
。在这种情况下,E
是AsyncResult
在此处插入lambda将取代Handler.handle
。因此,单参数res
必须是AsyncResult
类型。这就是为什么它可以调用AsyncResult.successed
而不会出现问题
简单地说:
listen
的第三个参数不可能不是处理程序
,因此lambda必须提供类型为
的参数
编辑:
关于在lambda中使用嵌套泛型,考虑这个类:
public class myClass<T> {
public void doSomething(int port, String host, Handler<AsyncResult<T>> handler) {
//Stuff happens
}
}
在这种情况下,编译器仍然可以将res
推断为AsyncResult
,因为在这种情况下T
是String
。如果我打开AsyncResult
,我就可以调用String
方法,比如toUpperCase
等等
如果您最终引用了一个MyClass
并尝试类似地使用lambda,res
将被推断为AsyncResult
。(您可以打开?
类型,但由于在编译时无法知道它的类型,因此必须将其视为对象
)
如果我们在声明过程中没有声明泛型类型,我们将得到一个关于它的警告,并且作为原始类型的结果,此代码将无法工作(感谢Holger):
因为我们已经将myObj
声明为MyClass
的原始类型,res
成为对象的类型(而不是AsyncResult
),所以我们不能对其调用succeed
通过这种方式,在不确切知道使用lambda的参数推断的类型的情况下,使用lambda是不可能的
可能有一些高级方法可以使用lambda来代替泛型类型在其自身签名中声明的方法,但我需要做一些研究来说明这些要点。本质上,即使可能发生这种情况,您也需要调用MyClass.doSomethingStatic
,以便在声明lambda之前声明类型,从而可以推断类型
简单地说:
不能在无法推断类型的情况下使用lambda。泛型不会改变这一点。Well方法签名确切说明了第三个参数的类型。res
以前不是声明过吗?@Tomas你在问编译器是如何工作的吗?类型推断?阅读类型推断-简而言之,编译器(不是JVM)说:“这个方法接受一个处理程序,这是lambda的有效接口,这个lambda与处理程序的唯一方法具有相同数量的输入;因此我可以假设lambda是处理程序的。”当然,细节更复杂,正确的,OPs的误解是,Handler
是从res
推导出来的,但实际上,res
是通过使用listen
方法的声明推断出来的Handler
。这是非常有价值的答案,谢谢。但是,如果我们在那里添加额外级别的泛型,那该怎么办呢:它不是Handler
,而是Handler
?然后我假设,只有在使用lambda的情况下,才允许使用AsyncResult
中的接口函数?@Tomas您肯定是在正确的轨道上,尽管我想不出一个实例,您可以在不事先声明t
的情况下强制执行泛型类型t
。也许更清楚的假设是,如果使用AsyncResult
,其中如果打开对象
,则无论实际的底层对象类型是什么,都只能使用对象
的方法。如果这让你感到困惑,我可以进一步解释。@Zront推断类型的不是JVM,而是编译器更正:原始类型MyClass
没有被视为MyClass
。原始类型根本不使用泛型。相反,接口的推断类型将是原始类型Handler
,res
将不是AsyncResult
,而是对象。这就是为什么永远不要使用原始类型。如果使用表示未知类型参数的MyClass
,泛型将起作用,res
将是AsyncResult
。
public class myClass<T> {
public void doSomething(int port, String host, Handler<AsyncResult<T>> handler) {
//Stuff happens
}
}
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!");
}
});
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!");
}
});