Lambda表達式


一:簡介

JDK的升級的目的有以下幾個:增加新的功能、修復bug、性能優化、簡化代碼等幾個方面,Lambda表達式就是屬於簡化代碼,用於簡化匿名實現類,提供一種更加簡潔的寫法。Lambda表達式在Swift語言中稱之爲代碼塊,Lambda表達式可以認爲是一種特殊的接口,該接口必須只有一個抽象方法。

語法

(參數類型 參數名, 數參數類型 參數名2...) -> { 
    // code
};
  • 1
  • 2
  • 3

小括號()中的內容就是方法中的參數列表包括參數類型、參數名,其中參數類型是可以省略的,當參數個數只有一個時也可以省略掉小括號;
花括號{}中的內容就是方法中的方法體,當方法體中只有一行代碼時可以省略{},當方法體中只有一行代碼並且需要返回值時也可以省略掉return;
由於Lambda表達式是匿名實現類的簡寫,是一種特殊的接口,當賦值給一個變量時也少不掉分號;

Lambda表達式的作用

  • 簡化匿名實現類的書寫
  • 作爲函數中的參數來傳遞

二:示例

示例1:兩個參數,一個返回值

IHello 一個很普通的接口,但接口中只能有一個抽象方法

public interface IHello {
    String sayHello(String name, String msg);
}
  • 1
  • 2
  • 3

Main

public class Main {
    public static void main(String[] args) {
        // 將一個Lambda表達式賦值給一個接口,說明Lambda表達式就是一種接口數據類型,只不過該接口只能有一個抽象方法
        // 參數列表可以省略參數類型,可以寫成(name, msg),
        // 在JDK1.8中有個叫做類型推斷的東西,可以自動推斷出參數的類型,
        // 因爲IHello中只有一個抽象方法,知道方法了就知道參數列表了,從而就能推出參數類型來
        IHello iHello = (String name, String msg) -> {
            String hello = name + ": " + msg;
            return hello;
        };
        // 調用接口的方法
        String content = iHello.sayHello("mengday", "happy new year everyone!");
        System.out.println(content);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

示例2:一個參數,一個返回值

public interface IHello {
    String sayHello(String name);
}
  • 1
  • 2
  • 3

Main

public class Main {
    public static void main(String[] args) {
        // 參數列表可以省略參數類型,當只有一個參數時可以省略小括號 (String name) --> (name) --> name
        // 當方法體中只有一行代碼並且有返回值時可以同時省略花括號和return
        // { return name + ": " + "happy new year everyone!";} --> name + ": " + "happy new year everyone!";
        IHello iHello = name -> name + ": " + "happy new year everyone!";

        String content = iHello.sayHello("mengday");
        System.out.println(content);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

示例3:沒有參數,沒有返回值

public interface IHello {
    void sayHello();
}
  • 1
  • 2
  • 3
public class Main {
    public static void main(String[] args) {
        // 當表達式沒有參數時一對小括號是不能省略掉的
        IHello iHello = () -> System.out.println("mengday: happy new year everyone!");
        iHello.sayHello();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

從這三個示例中我們發現我們只定義了接口,並沒有定義實現類,而是通過Lambda表達式來代替了實現類。
注意:Lambda接口只能有一個抽象方法,可以同時擁有多個靜態方法和默認方法。


示例4:Lambda表達式參數

public interface IHello {
    void sayHello(String name);
}
  • 1
  • 2
  • 3
public class Main {

    public static void sayHello(IHello iHello, String name) {
        iHello.sayHello(name);
    }

    public static void main(String[] args) {

        IHello iHello = name -> {
            String content = name + ": " + "happy new year everyone!";
            System.out.println(content);
        };

        // 這裏可以把iHelo看成一個匿名實現類來傳遞參數
        sayHello(iHello, "mengday");

        // 如果去掉變量的接收,直接將Lambda表達式傳遞到參數中,此時Lambda表達式更像是一個函數
        // 也就是說JDK1.8竟然可以將一個函數作爲參數傳遞到方法中,這是之前版本做不到的
        // 將函數作爲方法的參數傳遞,一般用於回調函數,將回調函數傳遞到方法中
        sayHello(name -> {
            String content = name + ": " + "happy new year everyone!";
            System.out.println(content);
        }, "mengday");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

示例5:集合排序示例

public static void main(String[] args) {

     // 寫法一:使用匿名內部類
     // 好好學習,天天向上
     List<String> words = Arrays.asList("good", "good", "study", "day", "day", "up");

     // public static <T> void sort(List<T> list, Comparator<? super T> c)
     Collections.sort(words, new Comparator<String>() {
         @Override
         public int compare(String s1, String s2) {
             // 降續排序
             return s2.compareTo(s1);
         }
     });

     System.out.println(words);


     // 寫法二:使用Lambda表達式
     // 咱倆誰跟誰
     words = Arrays.asList("we", "two", "who", "and", "who");
     Collections.sort(words, (String s1, String s2) -> {return s2.compareTo(s1);});
     System.out.println(words);


     // 寫法三:使用Lambda表達式(簡寫)
     // 有事起奏,無事退朝
     words = Arrays.asList("if", "you", "have", "something", "to", "say", "then", "say!",
             "if", "you", "have", "nothing", "to", "say", "go", "home!");
     Collections.sort(words, (s1, s2) -> s2.compareTo(s1));
     System.out.println(words);
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

函數式接口@FunctionalInterface


從我們自定義的IHello示例來看,Lambda表達式其實是一種接口類型的數據類型,嚴格的說Lambda表達式的數據類型是:函數式接口,是一種特殊的接口,該接口使用@FunctionalInterface註解來標記(不是必須的,可以不用該註解標記,IHello接口就沒有使用該註解標記, ),並且接口中只能有一個抽象方法,可以有多個靜態方法或者默認方法, 每一個該類型的lambda表達式都會被匹配到這個抽象方法。

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    // 其它static、default方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

@FunctionalInterface: 該註解沒啥太大含義,該註解是給編譯器做檢查使用的,如果使用了該註解,編譯器就會檢查該接口中的抽象方法是不是隻有一個,如果有多個就會報錯:在接口Xxx中找到多個非覆蓋抽象方法

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
  • 1
  • 2
  • 3
  • 4

我們完善一下我們的IHello, 使用@FunctionalInterface註解

@FunctionalInterface
public interface IHello {
    void sayHello(String name);
}
  • 1
  • 2
  • 3
  • 4

我們可以將lambda表達式當作任意只包含一個抽象方法的接口類型,也就是說我們的IHello接口無論叫什麼名字,接口中的方法無論叫什麼名字都無所謂(只是可讀性更好些),因此可以再進行抽象化一下,JDK1.8中提供了這樣的函數式接口,我們也不需要再定義IHello接口了,JDK1.8中提供了Supplier、Consumer、Function、BiFunction,這幾個是比較常用的

Supplier< T > 供應商:沒有參數,有返回值

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • 1
  • 2
  • 3
  • 4

Consumer< T > 消費者: 只有一個參數,沒有返回值

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  • 1
  • 2
  • 3
  • 4

Function< T, R > 函數:一個參數,一個返回值

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
  • 1
  • 2
  • 3
  • 4

BiFunction< T, U, R > 二元函數:兩個參數,一個返回值

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}
  • 1
  • 2
  • 3
  • 4

Comparator< T > 比較器:接收兩個參數,返回比較的結果

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}
  • 1
  • 2
  • 3
  • 4

使用以上四大函數式接口來取代自定義的接口IHello

public class Main {
    private static String end = ".";

    public static void main(String[] args) {
        // 直接使用JDK1.8提供的接口,不需要再定義IHello接口, 直接使用JDK提供的接口來接收Lambda表達式
        Supplier<String> supplier = () -> "mengday: happy new year everyone!";
        String result = supplier.get();
        System.out.println(result);

        Consumer<String> consumer = (name) -> System.out.println(name + ": " + "happy new year everyone!");
        consumer.accept("mengday");

        Function<String, String> func = (name) -> name + ": " + "happy new year everyone!";
        String hi = func.apply("mengday");
        System.out.println(hi);


        // 在代碼塊的內部可以訪問靜態全局變量
        // 在代碼塊中可以訪問外邊局部變量
        // 在代碼塊的內部可以修改全局靜態變量
        // 在代碼塊內部是不能訪問接口中的其它方法的
        String split = ": ";
        BiFunction<String, String, String> biFunction = (String name, String msg) -> {
            end = "!";
            String hello = name + split + msg + end;
            return hello;
        };
        String hello = biFunction.apply("mengday", "happy new year everyone");
        System.out.println(hello);

        // 根據字符串長度比較大小
        Comparator<String> comparator = (s1, s2) -> s1.length() - s2.length();
        int compare = comparator.compare("abc", "ab");
        System.out.println(compare);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Predicate< T > 斷言 謂詞: 用於測試一個條件的真假


package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {
    // 在給定的參數上評估這個謂詞
    boolean test(T t);

    // 返回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯AND
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    // 返回表示此謂詞的邏輯否定的謂詞,相當於not
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    // 返回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯或
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    // 返回根據 Objects.equals(Object, Object)測試兩個參數是否相等的謂詞
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

Main

public static void main(String[] args) {
    // 可以構造複雜的條件: 並且and、或者or、否negate
    String email = "[email protected]";
    Predicate<String> predicate = (str) -> str.length() > 20;

    // 測試 emial.length > 0 的boolean
    boolean result = predicate.test(email);     // false

    // 測試 !(emial.length > 0) 的boolean
    result = predicate.negate().test(email);    // true

    Predicate<String> orPredicate = (str) -> str.contains("@");
    // 測試 emial.length > 0 or emial.contains("@")
    result = predicate.or(orPredicate).test(email);     // true
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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