簡體   English   中英

Java 8如何推斷lambdas參數類型

[英]How Java 8 is able to infer lambdas argument type

我目前正在使用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如何從諸如res之類的非信息對象中推斷出Handler<AsyncResult<NetServer>>之類的通用數據類型? 對於像JavaScript這樣執行鴨式打字的語言來說,這似乎很好,但是對於像Java這樣執行強類型化的語言來說,這對我來說並不那么明顯。 如果我們使用匿名類而不是lambda,則所有數據類型都將存在。

-編輯-正如@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());
});

如果類型不可用,則只能使用FutureAsyncResults上可用的方法。

編譯器以與您完全相同的方式推斷類型。

Netserver.listenHandler<AsyncResult<NetServer>>作為其第三個參數。

Handler是具有一個方法handle(E event)的vertx FunctionalInterface。 在這種情況下, EAsyncResult<NetServer>

在此處插入一個lambda使其代替Handler.handle 因此,單個參數res必須為AsyncResult<NetServer>類型。 這就是為什么它可以隨后成功調用AsyncResult.succeeded原因。

只是:

除了Handler<AsyncResult<NetServer>>listen的第三個參數是不可能的,因此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 ,則可以調用諸如toUpperCase類的String方法,諸如此類。

如果最終引用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代替以自己的簽名聲明泛型的方法,但是我需要做一些研究來說明這些觀點。 本質上,即使可能發生這種情況,您也需要在聲明lambda之前調用MyClass.<MyType>doSomethingStatic來聲明類型,以便可以推斷類型。

只是:

您不能使用無法推斷類型的Lambda。 泛型不會改變這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM