簡體   English   中英

Java 的 lambda 語法的細分是什么?

[英]What is the breakdown for Java's lambda syntax?

請解釋 Java 8 的 lambda 方法的語法。

有很多關於 lambda 函數是什么的解釋,但我找不到對語法的徹底解釋,而且我發現學習正確復制語法非常困難,因為我不明白為什么它們按原樣重新編寫。

這是我遇到的一個常見案例,由 NetBeans 提供:

public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> {
        new MainAppJFrame();
    });
}

因此,不知何故,以下 lambda 表達式解析為匿名Runnable對象的 run() 方法:

() -> {
    // do stuff
}

->是 lambda 語法正確的,對吧? 大括號只是包含匿名方法代碼。 括號是否為空參數,因為在這種情況下我們正在創建一個Runnable.run()方法?

這對我來說是相當不清楚的。 我假設編譯器知道根據SwingUtilities.invokeLater(Runnable)方法預期的類型實例化一個匿名Runnable嗎? 如果有兩個僅參數列表不同的SwingUtilities.invokeLater方法會發生什么? 顯然在這種特定情況下沒有,但在其他地方是可能的:

interface ExampleLambdaConsumer {
    public void doSomething(Runnable r);
    public void doSomething(java.lang.reflect.Method m);
}

class C implements ExampleLambdaConsumer {
    // implementations for doSomething methods here

    public static void main(String[] args) {
        doSomething(() -> {
            // lambda method body here
        }
    }
}

語法是:

arguments -> body

其中arguments可以是

  • ()

  • 如果可以從上下文推斷出該變量的類型,則為單個變量

  • 括號中的變量序列,帶或不帶類型(或自 Java 11 起,帶var )。
    示例: (x) , (x, y) , (int x, int y) , (var x, var y) (Java 11+)。
    以下無效: (int x, y) , (x, var y) , (var x, int y)

body可以是表達式或帶有語句的{...}塊。 表達式(除了方法或構造函數調用)只是簡單返回,即() -> 2等價於() -> {return 2;}


對於像() -> f()這樣的 lambda 表達式() -> f()主體是方法或構造函數調用表達式):

  • 如果f()返回void ,它們等價於() -> { f(); } () -> { f(); }

  • 否則,它們等價於() -> { f(); } () -> { f(); }() -> { return f(); }) () -> { return f(); }) 編譯器從調用上下文推斷它,但通常它更喜歡后者。

因此,如果您有兩種方法: void handle(Supplier<T>)void handle(Runnable) ,則:

  • handle(() -> { return f(); })handle(() -> x)將調用第一個,

  • handle(() -> { f(); }將調用第二個,並且

  • handle(() -> f())

    • 如果f()返回void或不可轉換為T的類型,則它將調用第二個

    • 如果f()返回可轉換為T的類型,則它將調用第一個


編譯器嘗試將 lambda 的類型與上下文匹配。 我不知道確切的規則,但答案是:

如果有兩個僅參數列表不同的 SwingUtilities.invokeLater 方法會發生什么?

是:這取決於這些參數列表是什么。 如果另一個invokeLater也恰好有一個參數,並且該參數的類型也是具有void*()類型方法的接口,那么它會抱怨它無法弄清楚您的意思是哪種方法。

為什么它們是這樣寫的? 嗯,我認為這是因為 C# 和 Scala 中的語法幾乎相同(它們使用=>而不是-> )。

語法是

(parameter_list_here) -> { stuff_to_do; }

如果它是單個表達式,則可以省略大括號。 如果參數列表是單個參數,則可以省略參數列表周圍的正則括號。

該語法僅適用於所有功能接口。 @FunctionalInterface 注釋告訴編譯器您打算編寫這樣的接口,如果您不滿足要求,則會給出編譯錯誤 - 例如它必須只有 1 個可覆蓋的方法。

@FunctionalInterface
interface TestInterface {
    void dostuff();
}

Runnable 也是這樣聲明的。 其他接口不是,它們不能與 lambda 函數一起使用。

既然我們已經使用不帶參數的方法創建了一個新的函數式接口,那么我們如何測試您對簽名中“沖突”的問題?

public class Main {
    private void test(Runnable r) {

    }
    private void test(TestInterface ti) {

    }
    public static void main(String[] args) { 
        test(() -> { System.out.println("test");})
    }

    @FunctionalInterface
    interface TestInterface {
        void dostuff();
    }
}

結果:編譯錯誤:對方法測試的調用不明確。

您會看到,編譯器/VM(如果運行時完成)找到適當的方法及其參數列表,並查看參數是否是功能接口,如果是,則創建該接口的匿名實現。 從技術上講(在字節碼中)它與匿名類不同,但在其他方面是相同的(您不會看到 Main$1.class 文件)。

您的示例代碼(由 Netbeans 提供)也可以替換為

SwingUtilities.invokeLater(MainAppJFrame::new);

順便提一句。 :)

Java 8 中基本上采用了 Lambda 表達式,以將覆蓋過程函數簡化為匿名函數

它們只是覆蓋舊的 Java 匿名函數的快捷方式。

請參考以下示例:

假設您有接口 A ,它只有一個方法聲明如下:

interface A{        
    void print();           
}

現在使用舊的 Java樣式,我們將以匿名方式覆蓋它,如下所示:

new A() {           
        @Override
        public void print() {
            System.out.println("in a print method");                
        }           
};

另外現在使用 java 8 lambda 表達式,我們將使用它,如下所示:

() -> System.out.println("in a print method");

在這里,我們可以通過之前所需的方法參數->操作,然后重寫后身體->操作。

我們需要實現的唯一更多設置是我們需要使用@FunctionalInterface聲明接口,如下所示:

 @FunctionalInterface
 interface A{        
    void print();           
 }

注意: - lambda 表達式只能用於只有一個非默認方法的“功能”接口。

語法令人困惑。 它驅使我扭轉局面。 如果不是 Intellij,糾正我我會弄錯的。

編譯正常。

class Scratch {
    public static void main(String[] args) {
        Predicate<Integer> even = integer -> {return (integer%2 == 0);};
    }
}

編譯正常。

class Scratch {
    public static void main(String[] args) {
        Predicate<Integer> even = integer -> {return integer%2 == 0;};
    }
}

不編譯,因為它不再是一個語句。

class Scratch {
    public static void main(String[] args) {
        Predicate<Integer> even = integer -> {return integer%2 == 0};
    }
}

不編譯,因為它不再是一個語句並且 return 語句已被刪除。

class Scratch {
    public static void main(String[] args) {
        Predicate<Integer> even = integer -> {integer%2 == 0};
    }
}

編譯正常。

class Scratch {
    public static void main(String[] args) {
        Predicate<Integer> even = integer -> integer%2 == 0;
    }
}

所以這個故事的寓意是,如果你使用括號里面的內容必須是一個語句,並且它必須返回 Functional 接口單個抽象方法所期望的類型(即唯一一個不是默認值的類型)。

實際上,我為自己寫的比其他任何東西都多,因為它一直困擾着我。

暫無
暫無

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

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