Java8如何推断lambdas参数类型

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(

我目前正在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)
以文档中的用法为例:

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!");
  }
});