java - Java 8 如何推断 lambdas 参数类型

标签 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("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) .在这种情况下,EAsyncResult<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> ,因为 TString在这种情况下。如果我打开 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/

相关文章:

java - 为 Android 应用程序检测人脸?

c++ - 带有 lambda 自定义删除器的 std::unique_ptr 无法编译

java - 在 Java 8 中使用流、lambda

java - 删除字符后的所有内容,包括该字符

java - 读取属性文件时获取空指针

java - 无法通过 db2cmd 连接到 DB2 服务器

c# - FirstOrDefault 抛出异常而不是 null

c++ - 使用Lamda从QWebEngineView中的QWebEnginePage获取HTML

java - 如何在 Iterable 上执行 Stream 函数?

java - 即使我们目前不打算迁移到 Java 8+,我们也可以升级到 Spring 5 吗?