Lambda-讓人又愛又恨的“->"

語言: CN / TW / HK

theme: qklhk-chocolate highlight: agate


小知識,大挑戰!本文正在參與“  程式設計師必備小知識  ”創作活動

寫在前邊

  • 聊到Java8新特性,你第一反應想到的肯定是Lambda表示式和函式式介面的出現。要說他到底有沒有在一定程度上“優化”了程式碼的簡潔性呢?抑或是ta在一定程度上給程式設計師增加了閱讀和debug的難度,讓不少程式設計師頭疼。這期來接著“聊聊Java”,新特性篇只又愛又恨的Lambda

Lambda表示式

實質屬於函數語言程式設計的概念,可返回一個介面的實現

執行緒中的應用

image.png

傳統方式

建立一個一次性的類

```java //一次性的類,用在new Thread中充當Runnable對的實現類 class runnable implements Runnable{

@Override
public void run() {
    System.out.println("我在路上");
}

}

public class lambdaTest { public static void main(String[] args) { runnable runnable = new runnable(); Thread thread1 = new Thread(runnable); } } ```

(稍微優化)匿名內部類

java Thread thread1 = new Thread(new Runnable() { @Override public void run() { System.out.println("我在路上"); } });

作用

  • 避免匿名內部類定義過多
  • 使程式碼看起來簡潔
  • 簡化程式碼,只留下核心邏輯

函式式介面

定義:任何介面,如果只包含唯一一個抽象方法,那麼它就是一個函式式介面

java public interface Runnable{ void run(); } 為了避免後來人給這個介面新增函式後,導致該介面有多個函式,不再是函式式介面,我們可以在介面類的上方宣告@FunctionalInterface image.png

所有的Lambda的型別都是一個介面

  • 而Lambda表示式本身,就是這個介面的實現

image.png

如果定義成實現類,就會報錯

  • 我們在程式編譯的時候並不知道我們最終創建出來的物件具體是哪個子類,直到執行時期才能得知,隨之我們可以讓這個like指向指向各種不同的類上,可以呼叫各種不同的子類方法,大大提高了程式的可擴充套件性

    而這裡我們用lambda實際上是等價於匿名內部類(沒有類名),實際創建出來的類是什麼,我們不知道,所以我們會定義成介面,利用多型的向上轉型特性 關於多型的更多特性,在我的另一篇部落格中 : 傳送門->

image.png image.png

方法引用

Demo

java //介面定義 interface parseIntNum{ //定義一個String轉化成Integer的方法 int pass(String s); } public static void main(String[] args) { parseIntNum parseIntNum1; parseIntNum parseIntNum2; //原始lambda parseIntNum1 = (str)-> Integer.parseInt(str); System.out.println(parseIntNum1.pass("1")); //方法引用改進版本 parseIntNum2 = Integer::parseInt; System.out.println(parseIntNum2.pass("1")); } image.png 所謂方法引用,是指如果某個已經存在的方法,他的簽名接口裡邊定義的函式恰好一致,就可以直接傳入方法引用。 因為parseIntNum介面定義的方法是int pass(String s),和Integer中的靜態方法int parseInt(String s)相比,除了方法名外,方法引數一致,返回型別相同,這就是我們說的方法簽名一致,可以直接傳入方法引用

方法引用的寫法

  • 其實很簡單,只需要使用操作符雙冒號 "::"

常見的方法引用

image.png

常見的引用形式

類名::靜態方法名

呼叫類的靜態方法

  • 其實我們上邊使用Integer::parseInt 就等價於呼叫 Integer的靜態方法 parseInt

image.png

物件:例項方法

此處小小中二了一點hhhh

java class Naruto{ public static void Rasengan(){ System.out.println("螺旋丸"); } } //介面定義 interface Ninja{ //定義一個奧義方法 void aoyi(); } public class methodQuote { //通過引用Naurto的螺旋丸 Ninja ninja=Naruto::Rasengan; //再發動奧義 ninjia.aoyi(); }

資料型別:new

```java public static void main(String[] args) { //方式一 IntFunction arr1 = new IntFunction() { @Override public int[] apply(int num) { return new int[num]; } }; arr1.apply(10); //方式二(方法引用) IntFunction arr2 = int[]::new; arr2.apply(10);

}

```

  • 除此之外還有很多種形式,這裡就不過多贅述了

開發常用

常用小技巧:遍歷陣列列印

對比三種方法,可以看出簡潔程度

java ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); //匿名內部類 arrayList.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }); //lambda優化 arrayList.forEach((integer)-> System.out.println(integer)); //方法引用列印,用方法引用替代了我們的匿名內部類(相當於替代了lambda) arrayList.forEach(System.out::println);

疑問解決

一開始有個疑問,看起來方法引用省略了引數,那我們Intger.parseInt是去對誰操作?

  • 其實是自己混淆了lambda,lambda定義的時候那個引數,根本不是實際的引數

    可以說那個引數,只是為方法體服務的,只是方法體裡邊會用到. 而我們都用了方法引用了,前提就是引數和返回值一樣,方法體也是我們想要實現的內容,這時自然而然都不用我們寫方法體了,那方法體所依賴的引數也自然不用派上用場了

寫在最後

  • 最近要開始忙活專案了,對於這些基礎知識的更深入掌握,也許會放在以後準備面試的時候,目前理解不深,如有錯誤之處還望指出!

寫在最後

  • 本專欄還會斷斷續續更新一些Java基礎知識和麵經,提前為以後面試打下基礎!