[英]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());
});
如果類型不可用,則只能使用Future
和AsyncResults
上可用的方法。
編譯器以與您完全相同的方式推斷類型。
Netserver.listen
將Handler<AsyncResult<NetServer>>
作為其第三個參數。
Handler
是具有一個方法handle(E event)
的vertx FunctionalInterface。 在這種情況下, E
是AsyncResult<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>
,因為在這種情況下T
為String
。 如果我解開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.