樂字節-Java8新特性-Lambda表達式

上一篇文章咱們瞭解了Java8新特性-接口默認方法,接下來咱們聊一聊Java8新特性之Lambda表達式java

Lambda表達式(也稱爲閉包),它容許咱們將函數當成參數傳遞給某個方法,或者把代碼自己看成數據處理。不少語言(Groovy、Scala等)從設計之初就支持Lambda表達式。可是java中使用的是 匿名內部類代替。segmentfault

最後藉助強大的社區力量,找了一個折中的Lambda實現方案,能夠實現簡潔而緊湊的語言結構。閉包

一、匿名內部類到Lambda的演化

匿名內部類,即一個沒有名字的,存在於一個類或方法內部的類。當咱們須要用某個類且只須要用一次,建立和使用和二爲一時,咱們能夠選擇匿名內部類,省掉咱們定義類的步驟。dom

匿名內部類會隱士的繼承一個類或實現一個接口,或者說匿名內部類是一個繼承了該類或者實現了該接口的子類匿名對象。下面看一個匿名內部類的例子:ide

測試類中調用方法函數

package com.lotbyte.main;
/*
    定義和使用匿名內部類
 */
public class NoNameClass {
    public static void main(String[] args) {

        Model m = new Model(){
            @Override
            public void func() {
                System.out.println("方法的實現");
            }
        };
        m.func();
    }
}
// 須要被實現的接口
interface Model{
    void func();
}

二、Lambda快速使用

從某種意義上來講,Lambda表達式能夠看做是匿名內部類對象的簡寫形式。最簡單的Lambda表達式能夠由 用逗號分隔的參數列表、->符號和語句塊組成。測試

注意:此時匿名內部類只能實現接口,不能是繼承抽象類.spa

例如將上面的例子作一個簡化,使用Lambda的形式以下:設計

public class NonameClassForLambda {
    public static void main(String[] args) {
        // Lambda方式簡寫,方法實現能夠很簡單
        Model1 md = ()-> System.out.println("hello");
        md.func();
        
        // 也能夠是比較複雜的操做
        md = () -> {
            for (int i = 1; i <=5; i++) {
                System.out.println(i);
            }
        };
        md.func();
    }
}
// 接口
interface Model1{
    void func();
}

以上是一個簡單的Lambda的書寫形式,()中是形參列表,沒有則爲空括號, ->爲語法格式,以後則爲方法的實現(一條語句能夠直接書寫,當有多條語句時,須要使用{}進行包裹)。從這能夠看出在接口中必須只能存在一個抽象方法。code

注意:Lambda中必須有個接口

三、Lambda的形式

使用Lambda時,實現方法能夠有參數,也能夠有返回值,若是沒指定參數類型,則由編譯器自行推斷得出。

3.一、 無參帶返回值

生成[1,10]之間的任意整數

interface Model2{
    int func();
}
Model2 md2 = () -> {return (int)(Math.random()*10+1)};

說明:Lambda的改寫須要有對應的抽象方法,當沒有參數時須要使用()佔位,當表達式只有一行代碼時,能夠省略return和{}

以上的Lambda等價於:

Model2 md2 = () -> (int)(Math.random()*10+1);

3.2 、帶參帶返回值

返回一個對數字描述的字符串:

interface Model3{
    String func(int a);
}
Model3 md3 = (int a) -> {
    return "This is a number " + a;
};

說明:形參寫在()內便可,參數的類型能夠省略,此時將由編譯器自行推斷得出,同時還能夠省略()

md3 =  a -> "This is a number " + a;

省略了參數類型,小括號,同時連帶實現體的括號和return都省了。

3.3 、帶多個參數

根據輸入的運算符計算兩個數的運算,並返回結果:

interface Model4{
    String func(int a, int b, String oper);
}
 Model4 md4 = (a, b, s) -> {
      String res = "";
      if("+".equals(s)){
            res = ( a+b ) + "";
      }else if("-".equals(s)){
            res = ( a-b ) + "";
      }else if("*".equals(s)){
            res = ( a*b ) + "";
      }else if("/".equals(s)){
            res = ( a/b ) + ""; // 暫不考慮除0的狀況
      }else{
            res =  "操做有失誤";
      }
      return res;
 };
 System.out.println(md4.func(1,1,"+"));

以上例子爲多個參數的Lambda表達式,其中省略掉了每個參數的類型,編譯器自動推斷。多條語句時實現體的{}不能省。

四、Lambda做爲參數

在jdk8以前,接口能夠做爲方法參數傳入,執行時必須提供接口實現類的實例。從java8開始,Lambda能夠做爲接口方法實現,看成參數傳入,不管從形式上仍是實際上都省去了對象的建立。使代碼更加的緊湊簡單高效。
使用Lambda表達式須要有如下幾步:
​ 一、定義接口,抽象方法的模板;
​ 二、在某方法中須要接口做爲參數;
​ 三、調用方法時須要將抽象方法實現(此時咱們使用Lambda表達式)並傳入便可。

4.一、定義接口

在接口中,必須有且僅有一個抽象方法,以肯定Lambda模板

// 無參無返回值的方法
interface LambdaInterface1{
    void printString();
}
// 帶參無返回值的方法
interface  LambdaInterface2{
    void printString(String str);
}
4.二、定義方法接收參數
在某方法中須要使用接口做爲參數
// 無參
public static void testLambda(LambdaInterface1 lam1){
    lam1.printString();
}
​
// 帶參
public static void testLambda2(String s,LambdaInterface2 lam2){
    lam2.printString(s);
}

4.三、Lambda實現

使用方法時須要用Lambda將抽象方法實現
使用方法時須要用Lambda將抽象方法實現

// 無參Lambda做爲參數
testLambda(()->{
    System.out.println("能夠簡單,能夠複雜");
});
// 帶參Lambda做爲參數
testLambdaParam("hello",(a)->{
    System.out.println(a);
});

經過以上三步,可以完整地展現Lambda從和演變而來。此後在使用時,jdk中已經提供不少場景了,即前兩部已經完成,咱們更多的是實現第三步便可。

五、forEach展現Lambda

例如以ArrayList的遍歷爲例子,分析Lambda的使用方式。

public static void main(String[] args) {
    List<String> strs = new ArrayList<String>(){
        {
            add("aaa");
            add("bbb");
            add("ccc");
        }
    };
    strs.forEach((str)-> System.out.println(str));
}

下面看看forEach的源碼,定義中使用了接口Consumer做爲參數,並調用了其方法:
圖片描述
Consumer中的抽象方法只有accept一個
圖片描述

經過在forEach方法中調用Consumer的accept方法,並將每個元素做爲參數傳入,使得accept方法能夠對每個元素進行操做,當咱們使用Lambda實現accept時就變成了咱們本身對每個元素的處理了。咱們只負責處理便可。

六、Lambda中使用變量

在Lambda中能夠定義本身的局部變量,也可使用外層方法的局部變量,還可使用屬性。這一點也不難理解,既然是一個方法的實現,只寫了一個代碼塊,那麼使用自己所屬方法的局部變量和類的屬性也並不過度。

public static void main(String[] args) {
    List<String> strs = new ArrayList<String>(){
        {
            add("aaa");
            add("bbb");
            add("ccc");
        }
    };
    int j = 1;
    strs.forEach((str)->{
        int i = 0;
        System.out.println(str + "  " + i + "  " + j);
    });
}

注意:此時外部局部變量將自動變爲final

七、Lambda做爲方法返回值

例子:返回判斷字符串是否爲空

public class Demo004_2 {
    public static void main(String[] args) {
        System.out.println(testLambda().isEmpty("string"));
    }
​
    // 判斷字符串是否爲空
    public static AssertEmpty testLambda(){
        return (n)-> null==n||n.trim().isEmpty(n);
    }
}
​
interface AssertEmpty{
    boolean isEmpty(String str);
}

今天關於Java8新特性-Lambda表達式就講到這裏了,接下來我會繼續講述Java8新特性之函數式接口。敬請繼續關注!歡迎留言評論。

相關文章
相關標籤/搜索