java8 lambda表達式


【前言】 java8新特性

java8 函數接口

java8 Optional使用總結

Java 8 時間日期使用

 

java8中一個非常重要的特性就是lambda表達式,我們可以把它看成是一種閉包,它允許把函數當做參數來使用,是面向函數式編程的思想,一定程度上可以使代碼看起來更加簡潔。例如以前我們使用匿名內部類來實現代碼:

     //匿名內部類寫法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("內部類寫法");
            }
        }).start();

使用lambda則更加簡潔:

        //lambda 寫法
        new Thread(() -> System.out.println("lambda寫法")).start();    

1、lambda表達式語法

    (paramters) -> expression;

或者

    (paramters) -> {statements;}  
    展開如:
    (Type1 param1, Type2 param2, Type2 param2, ...) -> {
        statement1;
        statement2;
        statement3;
        ...
        return statementX;
    }   

2、lambda表達式特征

  •  可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  •  可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
  •  可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  •  可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

示例:

        //入參為空
        TestDemo no_param = () -> "hi, no param";
        TestDemo no_param2 = () -> { return "hi, no param"; };
        System.out.println(no_param.hi());

        //單個參數
        TestDemo2 param = name -> name;
        TestDemo2 param2 = name -> { return name;};
        System.out.println(param.hei("hei, grils"));

        //多個參數
        TestDemo3 multiple = (String hello, String name) -> hello + " " + name;
        //一條返回語句,可以省略大括號和return
        TestDemo3 multiple2 = (hello, name) -> hello + name;
        //多條處理語句,需要大括號和return
        TestDemo3 multiple3 = (hello, name) -> {
            System.out.println("進入內部");
            return hello + name;
        };
        System.out.println(multiple.greet("hello", "lambda"));

3、方法引用

有以下幾種類型

3.1 對象::實例方法,將lambda的參數當做方法的參數使用

objectName::instanceMethod

示例:

        Consumer<String> sc = System.out::println;
        //等效
        Consumer<String> sc2 = (x) -> System.out.println(x);
        sc.accept("618, 狂歡happy");

3.2 類::靜態方法,將lambda的參數當做方法的參數使用

ClassName::staticMethod

示例:

        //ClassName::staticMethod  類的靜態方法:把表達式的參數值作為staticMethod方法的參數
        Function<Integer, String> sf = String::valueOf;
        //等效
        Function<Integer, String> sf2 = (x) -> String.valueOf(x);
        String apply1 = sf.apply(61888);

3.3 類::實例方法,將lambda的第一個參數當做方法的調用者,其他的參數作為方法的參數。開發中盡量少些此類寫法,減少后續維護成本。

ClassName::instanceMethod

示例:

        //ClassName::instanceMethod  類的實例方法:把表達式的第一個參數當成instanceMethod的調用者,其他參數作為該方法的參數
        BiPredicate<String, String> sbp = String::equals;
        //等效
        BiPredicate<String, String> sbp2 = (x, y) -> x.equals(y);
        boolean test = sbp.test("a", "A");

4、構造函數

無參的構造方法就是類::實例方法模型,如:

        Supplier<User> us = User::new;
        //等效
        Supplier<User> us2 = () -> new User();
        //獲取對象
        User user = us.get();

當有參數時

        //一個參數,參數類型不同則會編譯出錯
        Function<Integer, User> uf = id -> new User(id);
        //或加括號
        Function<Integer, User> uf2 = (id) -> new User(id);
        //等效
        Function<Integer, User> uf3 = (Integer id) -> new User(id);
        User apply = uf.apply(61888);

        //兩個參數
        BiFunction<Integer, String, User> ubf = (id, name) -> new User(id, name);
        User 狂歡happy = ubf.apply(618, "狂歡happy");

5、繼承及實現具有相同默認方法的父類或接口問題

接口A:

public interface A {

    String hi();

    String greet();

    default void hello() {
        System.out.println("A.hello");
    }

}

接口B:

public interface B {

    String hi();

    String hh();

    default void hello() {
        System.out.println("B.hello");
    }

}

類C實現A,B:

public class C implements A, B{

    @Override
    public String hi() {
        return "C.hi";
    }

    @Override
    public String greet() {
        return "C.greet";
    }

    @Override
    public String hh() {
        return "C.hh";
    }

    /**
     * 子類優先繼承父類的方法, 如果父類沒有相同簽名的方法,才繼承接口的默認方法。
     * 編譯報錯解決1:覆蓋法
     */
    @Override
    public void hello() {
        System.out.println("C.hello");
    }

    /**
     * 編譯報錯解決2:指定實現的父接口
     */
//    @Override
//    public void hello() {
//        A.super.hello();
////        B.super.hello();
//    }

}

此時若不處理hello方法時,類C將編譯出錯,解決方式要么覆蓋,要么指定實現父接口的該方法。

進一步測試繼承具有相同方法的父類:

類D:

public class D {

    public void hello() {
        System.out.println("D.hello");
    }
}

類C繼承類D:

public class C extends D implements A, B{

    @Override
    public String hi() {
        return "C.hi";
    }

    @Override
    public String greet() {
        return "C.greet";
    }

    @Override
    public String hh() {
        return "C.hh";
    }

    /**
     * 子類優先繼承父類的方法, 如果父類沒有相同簽名的方法,才繼承接口的默認方法。
     * 編譯報錯解決1:覆蓋法
     */
//    @Override
//    public void hello() {
//        System.out.println("C.hello");
//    }

    /**
     * 編譯報錯解決2:指定實現的父接口
     */
//    @Override
//    public void hello() {
//        A.super.hello();
////        B.super.hello();
//    }

}

此時若不覆蓋或指定父接口的方法時,類C將繼承類D的hello方法。

6、總結

java8引入lambda表達式是接收了函數式編程語言的思想,例如scala之類的,它將函數視為一等公民,可以使用高階函數等。

和指令式編程相比,函數式編程強調函數的計算比指令的執行重要。

和過程化編程相比,函數式編程里函數的計算可隨時調用。
寫在最后,lambda表達式可以使代碼看起來簡潔,但一定程度上增加了代碼的可讀性以及調試的復雜性,所以在使用時應盡量是團隊都熟悉使用,要么干脆就別用,不然維護起來是件較痛苦的事。
 

源碼參照Github

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM