簡體   English   中英

Java - 為什么具有匹配簽名的 SAM 類型不可互換?

[英]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 特征。

涉及多個編譯時功能,這些功能在運行時不存在,而且在運行時執行起來也太昂貴了。

  • 編譯器使用類型推斷,結合泛型類型系統,來確定要實現的接口。

  • 然后它檢查接口的潛在復雜類型層次結構,包括哪個方法覆蓋哪個,再次考慮泛型類型系統的規則,最終確定哪些抽象方法(不包括privatedefaultstatic方法)不匹配java.lang.Objectpublic方法留待實施。

  • 如果只有一個方法要實現,則識別該方法的擦除和所有橋接方法,以包含在構建第一步中推斷的功能接口實例的配方中。

總體操作是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() )相同的行為。 因此, b2test()方法與a2test()方法具有不同的行為是一致的。

暫無
暫無

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

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