Java中的lambda表達式


lambda表達式:

new Thread(()-> System.out.println("Hello World!")).start();

為什么要使用lambda表達式?

  • 避免匿名內部類定義過多
  • 可以讓代碼看起來簡潔
  • 去掉了一堆沒有意義的代碼,只留下核心的邏輯

Functional Interface(函數式接口)

  • 任何接口,如果只包含唯一一個抽象方法,那么它就是一個函數式接口
public interface Runnable {
    public abstract  void run();
}
  • 對於函數式接口,可以通過lambda表達式來創建該接口的對象

函數式接口的推導

首先定義一個函數式接口

interface MyInterface {
    abstract void MyFunction(String str);
}

若要實現這個接口,正常情況下我們需要寫一個接口實現類,然后通過new這個類的對象來調用其中的方法:

//實現函數式接口
class MyClass implements MyInterface {
    @Override
    public void MyFunction(String str) {
        System.out.println(str);
    }
}

public class TestLambda {
    public static void main(String[] args) {
        //通過接口new一個MyClass對象,然后調用方法
        MyInterface myInterface = new MyClass();
        myInterface.MyFunction("接口實現類方法");
    }
}

為了調用接口中的方法,需要單獨定義一個接口的實現類,不是很方便,於是在我們在同一個類中定義靜態內部類:

public class TestLambda {
    //定義靜態內部內
    static class MyClass2 implements MyInterface {
        @Override
        public void MyFunction(String str) {
            System.out.println(str);
        }
    }
    public static void main(String[] args) {
        //創建MyClass2對象調用方法
        MyInterface myInterface = new MyClass2();
        myInterface.MyFunction("靜態內部類方法");
    }
}

可以看到,MyClass2定義在方法外面,依然有些繁瑣,於是我們開始使用局部內部類:

public class TestLambda {
    public static void main(String[] args) {
        //定義在函數內的局部內部類
        class MyClass3 implements MyInterface {
            @Override
            public void MyFunction(String str) {
                System.out.println(str);
            }
        }
        //創建MyClass3對象調用方法
        MyInterface myInterface = new MyClass3();
        myInterface.MyFunction("局部內部類方法");
    }
}

為了進一步簡化,我們還可以使用匿名內部類:

public class TestLambda {
    public static void main(String[] args) {
        //創建匿名內部類調用方法
        new MyInterface() {
            @Override
            public void MyFunction(String str) {
                System.out.println(str);
            }
        }.MyFunction("匿名內部類方法");
    }
}

演化到這里,代碼已經很精簡了,但是依然有繁瑣的代碼導致核心代碼依然不突出,為了進一步簡化,只突出核心代碼,我們使用了lambda來簡化:

接口 = (參數列表) -> {

        代碼;

};

其中,參數列表依據函數式接口中的方法參數而定,可以沒有,也可以有多個

lambda表達式使代碼達到了最簡,只保留了接口名,參數和執行體

public class TestLambda {
    public static void main(String[] args) {
        MyInterface myInterface = (String str) -> System.out.println(str);
        myInterface.MyFunction("lambda表達式方法");
    }
}

到此,我們清楚了lambda表達式的由來,最終的效果是我們定義一個函數式接口,通過一句lambda表達式就能調用里面的方法。lambda表達式使代碼達到了最精簡,只突出核心語句,去掉了沒有意義的代碼。

public class TestLambda {
    public static void main(String[] args) {
        MyInterface myInterface = (String str) -> System.out.println(str);
        myInterface.MyFunction("lambda表達式方法");
    }
}

//定義一個函數式接口
interface MyInterface {
    abstract void MyFunction(String str);
}

不知大家是否發現,文章最開頭給出的代碼是帶new關鍵字的,我們用自己寫的函數式接口不能用lambda函數作為參數把對象new出來,不知大家是否陷入了疑惑。別忘了,接口是不能被實例化的!如果一個接口能被實例化,只能說明他不是一個接口,而是一個類!

事實上,文章開頭給出的lambda表達式

new Thread(()-> System.out.println("Hello World!")).start();

是一個匿名內部類,Thread並不是函數式接口,而是函數式接口Runnable的實現類,因為接口是不能被實例化的,若Thread是函數式接口則不能使用new關鍵字創建對象。根據上述推理lambda的過程向上倒推,我們會發現實際上上述語句等價於:

new Thread() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        }.start();

通過查看Java源碼可以看到,Thread類中存在接收一個參數的構造方法,而start()方法調用了start0()方法,start0()又調用了run()方法,因此上述代碼又相當於:

new Thread() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        }.run();

實際測試運行結果也確實相同,到這里我們不難看出,這就是一個實現了一個接口並重寫接口中方法的匿名內部類。

那么新問題又來了,為什么說lambda表達式只支持函數式接口,是不是意味着函數式接口和實現了函數式接口的類都能用lambda表達式?事實上,lambda表達式作為參數值時,只需要形參類型為函數式接口即可,與類是否實現函數式接口無關。

public class Test02 {

    public static void main(String[] args) {
        //構造匿名內部類,lambda表達式作為參數
        new TestClass(() -> System.out.println("AAAA")) {
            @Override
            void tests() {
                System.out.println("BBBB");
            }
        }.tests();
    }
}

//TestClass類並沒有實現Runnable接口
class TestClass {
    //構造方法,接收參數類型為函數式接口Runnable
    public TestClass(Runnable runnable) {
    }

    void tests() {
        System.out.println("CCCC");
    }
}

當然,該段代碼純屬為了搞清楚語法規則,並沒有實際意義。因此,lambda表達式只支持函數式接口(只有一個抽象方法的接口)是因為lambda表達式作為參數時,只能用函數式接口作為形參來接收。

本人僅僅是因為對Java中的lambda感興趣進行研究后的記錄,把結果記錄並分享出來,如果對大家有任何幫助我感到萬分榮幸,因個人水平有限,文章中可能存在錯誤或者不正確的觀點,歡迎大家批評指出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM