Java8的Lambda表達式,你會不?

理解Lambda

Lambda表達式可以是一段可以傳遞的代碼,它的核心思想是將面向對象中的傳遞數據變成傳遞行爲,也就是行爲參數化,將不同的行爲作爲參數傳入方法。

隨着函數式編程思想的引進,Lambda表達式讓可以用更加簡潔流暢的代碼來代替之前冗餘的Java代碼。

口說無憑,直接上個例子吧。在Java8之前,關於線程代碼是這樣的:

class Task implements Runnable{

    @Override
    public void run() {
        System.out.println("Java8 之前 實現Runnable接口中的run方法");
    }
}
Runnable t = new Task();

我們定義了一個Task類,讓它實現Runnable接口,實現僅有的run方法,我們希望執行的線程體雖然只有一句話,但我們仍然花了大量大代碼去定義。爲了簡化,我們可以採用匿名內部類的方式:

Runnable taskBeforeJava8 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Java8 之前的寫法, 傳入匿名類");
    }
};

但是,其實還是不夠簡潔,我們用Lambda的寫法是這樣的:

// java8 之後
Runnable taskAfterJava8 = () -> System.out.println("Java8 之後的寫法,lambda表達式");

我們僅僅使用()->就完成了這件事,是不是非常簡潔呢?如果你覺得雖然Lambda寫法簡潔,但是它的規則讓人摸不着頭腦,那就跟着我接下去學叭。

基礎語法

(parameters) -> action
    (parameters) -> expression
    (parameters) -> {statements;}

parameters代表變量,可以爲空,可以爲單,可以爲空,你能想到的方式他都可以。

action是實現的代碼邏輯部分,可以是一行代碼expression,也可以是一個代碼片段statements。如果是代碼片段,需要加上{}

下面是一些合法的示例,你可以看看有沒有掌握:

表達式 描述
() -> 1024 不需要參數,返回值爲1024
x -> 2 * x 接收參數x,返回其兩倍
(x, y) -> x - y 接收兩個參數,返回它們的差
(int x, int y) -> x + y 接收兩個int類型參數,返回他們的和
(String s) -> print(s) 接收一個String對象,並打印

函數式接口

什麼是函數式接口?函數式接口是隻有一個抽象方法的接口,用作lambda表達式的類型。

@FunctionalInterface // 此註解作用的接口 只能擁有一個抽象方法
public interface Runnable {
    public abstract void run();
}

在這裏,@FunctionalInterface註解是非必須的,有點類似於@Override,起強調作用,如果你的接口標註該註解,卻沒有遵循它的原則,編譯器會提示你修改。

常用的函數式接口

JDK原生爲我們提供了一些常用的函數式編程接口,讓我們在使用他們編程時,不必關心接口名,方法名,參數名,只需關注它的參數類型,參數個數,返回值。

接口 參數 返回值 類別 示例
Consumer T void 消費型接口 打印輸出某個值
Supplier None T 供給型接口 工廠方法獲取一個對象
Function T R 函數型接口 獲取傳入列表的總和
Predicate T boolean 斷言型接口 判斷是否以summer爲前綴

消費型接口

    /**
     * 消費型接口, 傳入T 無返回值
     */
    public static void consumerTest() {
        Consumer<Integer> consumer = num -> System.out.println(num * num);
        consumer.accept(10);
    }

供給型接口

    /**
     * 供給型接口, 無參數,返回T
     */
    public static void supplierTest() {
        Supplier<Object> supplier = () -> new Object();
        System.out.println(supplier.get());
    }

斷言型接口

    /**
     * 斷言型 傳入參數T ,返回boolean
     */
    public static void predicateTest() {
        Predicate<String> predicate = name -> name.startsWith("summer");
        System.out.println(predicate.test("summerday"));
    }

函數型接口

    /**
     * 函數型接口 傳入T 返回R
     */
    public static void functionTest() {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        Function<List<Integer>, Integer> function = num -> {
            int res = 0;
            for (int n : list) {
                res += n;
            }
            return res;
        };
        Integer num = function.apply(list);
        System.out.println(num);
    }

方法引用

方法引用可以看作特定Lambda表達式的快捷寫法,主要分爲以下兩種:

  • 指向靜態方法的方法引用
  • 指向現有對象的實例方法的方法引用
/**
 * 方法引用
 * 1. 指向靜態方法的方法引用
 * 2. 指向現有對象的實例方法的方法引用
 *
 * @author Summerday
 */
public class MethodReferenceTest {
	
    public static List<String> getList(List<String> params, Predicate<String> filter) {
        List<String> res = new LinkedList<>();
        for (String param : params) {
            if (filter.test(param)) {
                res.add(param);
            }
        }
        return res;
    }
    // 靜態方法
    public static boolean isStartWith(String name) {
        return name.startsWith("sum");
    }

    public static void main(String[] args) {
        List<String> params = Arrays.asList("summerday","tqbx","天喬巴夏","summer","");
        //靜態方法的方法引用 getList(params, name -> MethodReferenceTest.isStartWith(name));
        List<String> list = getList(params, MethodReferenceTest::isStartWith);
        System.out.println(list);
        
        // 指向現有對象的實例方法的方法引用 getList(params, name -> name.isEmpty());
        List<String> sum = getList(params, String::isEmpty);
        System.out.println(sum);
        
    }
}

數組引用

/**
 * 數組引用
 * @author Summerday
 */
public class ArrayReferenceTest {

    public static void main(String[] args) {
        // 普通lambda
        Function<Integer,String[]> fun1 = x -> new String[x];
        String[] res1 = fun1.apply(10);
        System.out.println(res1.length);

        // 數組引用寫法
        Function<Integer,String[]> fun2 = String[]::new;
        String[] res2 = fun2.apply(10);
        System.out.println(res2.length);

    }
}

構造器引用

/**
 * 構造器引用
 * @author Summerday
 */
public class ConstructorReferenceTest {

    public static void main(String[] args) {

        // 普通lambda
        Supplier<User> sup = () -> new User();

        // 構造器引用
        Supplier<User> supplier = User::new;
        User user = supplier.get();
        System.out.println(user);
    }

}

class User{

}

總結

  1. lambda表達式沒有名稱,但有參數列表,函數主體,返回類型,可能還有一個可以拋出的異常的列表。
  2. lamda表達式讓你可以將不同的行爲作爲參數傳入方法。
  3. 函數式接口是僅僅聲明瞭一個抽象方法的接口。
  4. 只有在接受函數式接口的地方纔可以使用lambda表達式。
  5. lambda表達式允許你直接內聯,爲函數式接口的抽象方法提供實現,並將整個表達式作爲函數式接口的一個實例。
  6. Java8自帶了一些常用的函數式接口,包括Predicate,Function,Supplier,Consumer,BinaryOperator
  7. 方法引用讓你重複使用現有的方法實現並直接傳遞他們:Classname::method

參考閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章