【Java8新特性】Lambda表示式基礎語法,都在這兒了!!

冰河團隊發表於2020-05-06

寫在前面

前面積極響應讀者的需求,寫了兩篇Java新特性的文章。有小夥伴留言說:感覺Lambda表示式很強大啊!一行程式碼就能夠搞定那麼多功能!我想學習下Lambda表示式的語法,可以嗎?我的回答是:沒問題!這不,Lambda表示式來了!

匿名類到Lambda表示式

我們先來看看從匿名類如何轉換到Lambda表示式呢?

這裡,我們可以使用兩個示例來說明如何從匿名內部類轉換為Lambda表示式。

  • 匿名內部類到Lambda表示式

使用匿名內部類如下所示。

Runnable r = new Runnable(){
    @Override
    public void run(){
        System.out.println("Hello Lambda");
    }
}

轉化為Lambda表示式如下所示。

Runnable r = () -> System.out.println("Hello Lambda");
  • 匿名內部類作為引數傳遞到Lambda表示式作為引數傳遞

使用匿名內部類作為引數如下所示。

TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>(){
    @Override
    public int compare(Integer o1, Integer o2){
        return Integer.compare(o1, o2);
    }
});

使用Lambda表示式作為引數如下所示。

TreeSet<Integer> ts = new TreeSet<>(
	(o1, o2) -> Integer.compare(o1, o2);
);

從直觀上看,Lambda表示式要比常規的語法簡潔的多。

Lambda表示式的語法

Lambda表示式在Java語言中引入了 “->” 操作符, “->” 操作符被稱為Lambda表示式的操作符或者箭頭操作符,它將Lambda表示式分為兩部分:

  • 左側部分指定了Lambda表示式需要的所有引數。

Lambda表示式本質上是對介面的實現,Lambda表示式的引數列表本質上對應著介面中方法的引數列表。

  • 右側部分指定了Lambda體,即Lambda表示式要執行的功能。

Lambda體本質上就是介面方法具體實現的功能。

我們可以將Lambda表示式的語法總結如下。

1.語法格式一:無參,無返回值,Lambda體只有一條語句

Runnable r = () -> System.out.println("Hello Lambda");

具體示例如下所示。

@Test
public void test1(){
    Runnable r = () -> System.out.println("Hello Lambda");
    new Thread(r).start();
}

2.語法格式二:Lambda表示式需要一個引數,並且無返回值

Consumer<String> func = (s) -> System.out.println(s);

具體示例如下所示。

@Test
public void test2(){
    Consumer<String> consumer = (x) -> System.out.println(x);
    consumer.accept("Hello Lambda");
}

3.語法格式三:Lambda只需要一個引數時,引數的小括號可以省略

Consumer<String> func = s -> System.out.println(s);

具體示例如下所示。

@Test
public void test3(){
    Consumer<String> consumer = x -> System.out.println(x);
    consumer.accept("Hello Lambda");
}

4.語法格式四:Lambda需要兩個引數,並且有返回值

BinaryOperator<Integer> bo = (a, b) -> {
    System.out.println("函式式介面");
    return a + b;
};

具體示例如下所示。

@Test
public void test4(){
    Comparator<Integer> comparator = (x, y) -> {
        System.out.println("函式式介面");
        return Integer.compare(x, y);
    };
}

5.語法格式五:當Lambda體只有一條語句時,return和大括號可以省略

BinaryOperator<Integer> bo = (a, b) -> a + b;

具體示例如下所示。

@Test
public void test5(){
    Comparator<Integer> comparator = (x, y) ->  Integer.compare(x, y);
}

6.語法格式六:Lambda表示式的引數列表的資料型別可以省略不寫,因為JVM編譯器能夠通過上下文推斷出資料型別,這就是“型別推斷”

BinaryOperator<Integer> bo = (Integer a, Integer b) -> {
    return a + b;
};

等同於

BinaryOperator<Integer> bo = (a, b) -> {
    return a + b;
};

上述 Lambda 表示式中的引數型別都是由編譯器推斷得出的。 Lambda 表示式中無需指定型別,程式依然可以編譯,這是因為 javac 根據程式的上下文,在後臺推斷出了引數的型別。 Lambda 表示式的型別依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的“型別推斷”。

函式式介面

Lambda表示式需要函式式介面的支援,所以,我們有必要來說說什麼是函式式介面。

只包含一個抽象方法的介面,稱為函式式介面。

可以通過 Lambda 表示式來建立該介面的物件。(若 Lambda表示式丟擲一個受檢異常,那麼該異常需要在目標介面的抽象方法上進行宣告)。

可以在任意函式式介面上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函式式介面,同時 javadoc 也會包含一條宣告,說明這個介面是一個函式式介面。

我們可以自定義函式式介面,並使用Lambda表示式來實現相應的功能。

例如,使用函式式介面和Lambda表示式實現對字串的處理功能。

首先,我們定義一個函式式介面MyFunc,如下所示。

@FunctionalInterface
public interface MyFunc <T> {
    public T getValue(T t);
}

接下來,我們定義一個操作字串的方法,其中引數為MyFunc介面例項和需要轉換的字串。

public String handlerString(MyFunc<String> myFunc, String str){
    return myFunc.getValue(str);
}

接下來,我們對自定義的函式式介面進行測試,此時我們傳遞的函式式介面的引數為Lambda表示式,並且將字串轉化為大寫。

@Test
public void test6(){
    String str = handlerString((s) -> s.toUpperCase(), "binghe");
    System.out.println(str);
}

執行test6方法,得出的結果資訊如下所示。

BINGHE

我們也可以擷取字串的某一部分,如下所示。

@Test
public void test7(){
    String str = handlerString((s) -> s.substring(0,4), "binghe");
    System.out.println(str);
}

執行test7方法,得出的結果資訊如下所示。

bing

可以看到,我們可以通過handlerString(MyFunc<String> myFunc, String str)方法結合Lambda表示式對字串進行任意操作。

注意:作為引數傳遞 Lambda 表示式:為了將 Lambda 表示式作為引數傳遞,接收Lambda 表示式的引數型別必須是與該 Lambda 表示式相容的函式式介面的型別 。

寫在最後

如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Java8新特性。

最後,附上Java8新特性核心知識圖,祝大家在學習Java8新特性時少走彎路。

相關文章