[英]Java - Why are SAM types with matching signature not interchangable?
Java 可以正確推斷出 SAM 類型,並允許我傳遞 lambda 作為其實現的替代,但是當我嘗試將一種 SAM 類型轉換為具有相同簽名的另一種時,相同的機制失敗了:
public static Supplier<String> saySomething(){
return () -> "Hello";
}
@FunctionalInterface
interface Greeter {
String greet();
}
public static Greeter getGreeting1(){
// this compiles and runs just fine
return () -> saySomething().get();
}
public static Greeter getGreeting2(){
// this fails at compile:
// java: incompatible types: java.util.function.Supplier<java.lang.String> cannot be converted to Greeter
return saySomething();
}
public static Greeter getGreeting3(){
// this compiles without warnings(probably only because a Supplier<String> might
// also implement the Greeter interface, by chance) but if you call it will fail
// at runtime with a java.lang.ClassCastException
return (Greeter) saySomething();
}
我試圖理解為什么 JVM 不會讓 lambda 被視為這里的一件事和那里的另一件事,因為它只需要為此進行另一個 SAM 轉換。 我懷疑這是因為 lambda 被提升為隱藏的匿名類,但有人可以給出明確的答案嗎?
此外,是否有任何解決方法(除了將每個 SAM 轉換包裝在另一個 lambda 中,如第一個示例中所示)以允許它工作?
Lambda 表達式是 Java 語言工件,而不是 JVM 特征。
涉及多個編譯時功能,這些功能在運行時不存在,而且在運行時執行起來也太昂貴了。
編譯器使用類型推斷,結合泛型類型系統,來確定要實現的接口。
然后它檢查接口的潛在復雜類型層次結構,包括哪個方法覆蓋哪個,再次考慮泛型類型系統的規則,最終確定哪些抽象方法(不包括private
、 default
和static
方法)不匹配java.lang.Object
的public
方法留待實施。
總體操作是object 創建,盡管實現可能會重用現有對象:
在運行時,lambda 表達式的求值類似於 class 實例創建表達式的求值,只要正常完成會產生對 object 的引用。
相反,類型轉換是一種類型檢查表達式:
轉換表達式 […] 在運行時檢查引用值是否引用 object,其 class 與指定的引用類型或引用類型列表兼容 […]
結果,如果成功,仍然引用相同的 object。所以如果你有
interface A {
int test();
}
interface B extends A {
default int test() {
return method2() + 1;
}
int method2();
}
B b1 = () -> 10;
A a1 = (A)b1;
System.out.println(a1.test());
它將打印11
,因為變量a1
仍然包含對與b1
相同的B
實例的引用,並且其test()
方法實現為method2() + 1
。
相比之下,
A a2 = () -> 10;
System.out.println(a2.test());
將打印10
,因此,很明顯這不能與上面的b1
相同 object,無論 object 的創建方式如何(即相同的 lambda 表達式() -> 10
)。
將此A
實例轉換為B
,如B b2 = (B)a2;
由於兼容的功能簽名,假設它的method2()
像test()
一樣實現是不可能的,因為它會導致關於結果的test()
方法的矛盾行為(如果仍然不相信,想想會發生什么,如果我們再次將b2
轉換回A
)。
相反,當您使用方法引用時
B b2 = a2::test;
您明確表示b2
將與a2
不同 object 並且b2
的功能方法( method2()
)將表現出與a2
的功能方法( test()
)相同的行為。 因此, b2
的test()
方法與a2
的test()
方法具有不同的行為是一致的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.