1. 程式人生 > 實用技巧 >Java8之lambda表示式

Java8之lambda表示式

簡介

Lambda 表示式是 Java 1.8 跟 Stream 機制一同推出的。Lambda 表示式極大地減少了程式碼量,增加了程式碼的可讀性。
引入 Lambda 表示式之後,Java 開始支援把函式作為引數傳遞

前置條件

使用 Lambda 表示式的前置條件,作為引數的介面必須是函式式介面

  1. 首先型別必須是介面 interface,不能是類 class。比如,抽象類就不可以。
  2. 函式式介面有且僅有一個未被覆寫的抽象方法

舉例:

  • Object 中方法不算
// MyRunnable 仍然算是一個“函式式介面”
public interface MyRunnable extends Runnable {
    String toString();
    boolean equals(Object obj);
    int hashCode();
}
  • 介面中的 default 方法不算
// MyRunnable2 仍然算是一個“函式式介面”
public interface MyRunnable2 extends Runnable {
    default void run2() {}
}

FunctionalInterface 註解

@FunctionalInterface 可以幫助我們在編譯期識別出一個介面是否是“函式式介面”:

引數的傳遞

假如我們有一個如下含義的“函式式介面”:

@FunctionalInterface
public interface Formatter {
    void format(String name, int age);
}

我們可以構造一個測試:

public class LambdaTest {

    public static void main(String[] args) {
        print((String name, int age)-> System.out.println(String.format("name:%s age:%d", name, age)), "ziyu", 18);
    }

    public static void print(Formatter formatter, String name, int age) {
        formatter.format(name, age);
    }
}

多個引數

當有多個引數時,可以選擇省略所有引數型別宣告:

注意:
不能省略一部分保留一部分。(String name, age) -> System.out.println(name); 這是不合法的!

單個引數


當只有一個引數時,除了可以省略引數型別,還可以進一步省略掉括號。

編寫方式

沒有返回值

Runnable 就是一個常用的“函式式介面”,它的抽象方法 run() “沒有返回值”, 剛好適合用於此處的演示。測試例子如下:

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

    static void runIt(Runnable runnable) {
        new Thread(runnable).start();
    }
}

  • 如果寫成多行表示式,那麼需要 {} 來表示程式碼塊,且每一行程式碼結束時需要書寫 ; 表示語句的結束。
  • 如果程式碼塊中只有一條語句,那麼可以通過省略 {}; 來簡寫為單行表示式

有返回值

我們定義一個 IdFactory 介面來做演示:

public interface IdFactory {
    String generateId();
}

我們的示例程式碼如下:

import java.util.UUID;

public class LambdaTest3 {

    public static void main(String[] args) {
        String name = getId(()-> UUID.randomUUID() + "");
        System.out.println(name);
    }

    static String getId(IdFactory factory) {
        return factory.generateId();
    }

}

  • 如果寫成多行表示式,除了需要 {} 來表示程式碼塊,和每一行程式碼結束時需要書寫 ; 表示語句的結束以外,還應該在需要返回值的方法用 return 來返回值。
  • 如果程式碼塊中只有一條語句,那麼可以通過省略 {}; 以及 return 來簡寫為單行表示式

方法引用

比如我們要寫一段程式碼,用來打印出完整的加法表示式 a + b = c,並且要求根據 a 和 b 求出 c 作為函式返回值。
首先我們定一個“計算器”介面:

public interface Calculator {
    int compute(int a, int b);
}

接著我們寫一個測試用例:

public class LambdaTest3 {

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        // 這裡不能用 (a, b), 那樣的話會產生歧義,使得編譯器報錯
        runIt((x, y)->{
            System.out.print(x);
            System.out.print(" + ");
            System.out.print(y);
            System.out.print(" = ");
            System.out.println(x + y);
            return x + y;
        }, a, b);
    }

    static void runIt(Calculator calculator, int a, int b) {
        calculator.compute(a, b);
    }
}

靜態方法引用

  • 在 LambdaTest2 中定一個靜態方法 sum
  • 使用靜態方法引用 LambdaTest2::sum
  • 這樣在執行靜態方法 runIt 中的第一行 calculator.compute(a, b) 時,ab 會通過靜態方法引用傳遞給靜態方法 sum

通過這種方式,即簡化了引數的傳遞,也把“多行表示式”簡化為了“單行表示式”

成員方法引用

  • new LambdaTest2()::sum: 先新建物件 LambdaTest2,並且通過該物件來使用成員變數引用
    小貼士:
    如果先宣告變數 obj,再使用 obj::sum 也是合法的。
public static void main(String[] args) {
    int a = 10;
    int b = 20;
    LambdaTest2 obj = new LambdaTest2();
    runIt(obj::sum, a, b);
}

總結

  • Lambda表示式的前置條件:必須是“函式式介面”
  • 單個引數傳遞時,可以省略引數兩端的括號。引數的型別可以一起省略。
  • 編寫的方式主要包括單行表示式和多行表示式
  • 可以使用方法引用來把多行表示式寫成單行表示式。方法引用又包括了靜態方法引用和動態方法引用。