Lambda表達式超詳細總結

一、什麼是Lambda表達式

Lambda表達式,也可稱爲閉包。類似於JavaScript中的閉包,它是推動Java8發佈的最重要的新特性。

二、爲什麼使用Lambda表達式

我們可以把Lambda表達式理解爲一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。Lambda允許把函數作爲一個方法的參數,使用Lambda表達式可以寫出更簡潔、更靈活的代碼,而其作爲一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。

一個簡單示例:
使用傳統方法實現Runnable的run()方法,如下圖所示:
在這裏插入圖片描述
使用Lambda表達式實現,如下圖所示:
在這裏插入圖片描述
可以看到代碼明顯簡潔了許多。

三、Lambda表達式語法

Lambda表達式在Java語言中引入了一個操作符**“->”**,該操作符被稱爲Lambda操作符或箭頭操作符。它將Lambda分爲兩個部分:

  • 左側:指定了Lambda表達式需要的所有參數
  • 右側:制定了Lambda體,即Lambda表達式要執行的功能。

像這樣:

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

以下是lambda表達式的重要特徵:

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

下面對每個語法格式的特徵進行舉例說明:

(1)語法格式一:無參,無返回值,Lambda體只需一條語句。如下:

  @Test
  public void test01(){
    Runnable runnable=()-> System.out.println("Runnable 運行");
    runnable.run();//結果:Runnable 運行
  }

(2)語法格式二:Lambda需要一個參數,無返回值。如下:

  @Test
  public void test02(){
    Consumer<String> consumer=(x)-> System.out.println(x);
    consumer.accept("Hello Consumer");//結果:Hello Consumer
  }

(3)語法格式三:Lambda只需要一個參數時,參數的小括號可以省略,如下:

  public void test02(){
    Consumer<String> consumer=x-> System.out.println(x);
    consumer.accept("Hello Consumer");//結果:Hello Consumer
  }

(4)語法格式四:Lambda需要兩個參數,並且Lambda體中有多條語句。

  @Test
  public void test04(){
    Comparator<Integer> com=(x, y)->{
      System.out.println("函數式接口");
      return Integer.compare(x,y);
    };
    System.out.println(com.compare(2,4));//結果:-1
  }

(5)語法格式五:有兩個以上參數,有返回值,若Lambda體中只有一條語句,return和大括號都可以省略不寫

  @Test
  public void test05(){
    Comparator<Integer> com=(x, y)-> Integer.compare(x,y);
    System.out.println(com.compare(4,2));//結果:1
  }

(6)Lambda表達式的參數列表的數據類型可以省略不寫,因爲JVM可以通過上下文推斷出數據類型,即“類型推斷”

  @Test
  public void test06(){
    Comparator<Integer> com=(Integer x, Integer y)-> Integer.compare(x,y);
    System.out.println(com.compare(4,2));//結果:1
  }

類型推斷:在執行javac編譯程序時,JVM根據程序的上下文推斷出了參數的類型。Lambda表達式依賴於上下文環境。

語法背誦口訣:左右遇一括號省,左側推斷類型省,能省則省。

四、函數式接口

1.什麼是函數式接口

只包含一個抽象方法的接口,就稱爲函數式接口。我們可以通過Lambda表達式來創建該接口的實現對象。
我們可以在任意函數式接口上使用@FunctionalInterface註解,這樣做可以用於檢測它是否是一個函數式接口,同時javadoc也會包含一條聲明,說明這個接口是一個函數式接口。

2.自定義函數式接口

按照函數式接口的定義,自定義一個函數式接口,如下:

@FunctionalInterface
public interface MyFuncInterf<T> {
   public T getValue(String origin);
}

定義一個方法將函數式接口作爲方法參數。

  public String toLowerString(MyFuncInterf<String> mf,String origin){
    return mf.getValue(origin);
  }

將Lambda表達式實現的接口作爲參數傳遞。

  public void test07(){
      String value=toLowerString((str)->{
        return str.toLowerCase();
      },"ABC");
    System.out.println(value);//結果ABC
  }

3.Java內置函數式接口

四大核心函數式接口的介紹,如圖所示:
在這裏插入圖片描述
使用示例:
1.Consumer:消費型接口 void accept(T t)

  public void makeMoney(Integer money, Consumer<Integer> consumer){
    consumer.accept(money);
  }
 @Test
  public void test01(){
    makeMoney(100,t-> System.out.println("今天賺了"+t));//結果:今天賺了100
  }

2.Supplier:供給型接口 T get()

  /**
   * 產生指定的整數集合放到集合中
   * Iterable接口的forEach方法的定義:方法中使用到了Consumer消費型接口,
   *     default void forEach(Consumer<? super T> action) {
   *         Objects.requireNonNull(action);
   *         for (T t : this) {
   *             action.accept(t);
   *         }
   *     }
   */
  @Test
  public void test02(){
    List list = addNumInList(10, () -> (int) (Math.random() * 100));
    list.forEach(t-> System.out.println(t));
  }
  public List addNumInList(int size, Supplier<Integer> supplier){
    List<Integer> list=new ArrayList();
    for (int i = 0; i < size; i++) {
      list.add(supplier.get());
    }
    return list;
  }

3.Function<T,R>:函數型接口 R apply(T t)

 /**
   * 
   * 使用函數式接口處理字符串。
   */
   public String handleStr(String s,Function<String,String> f){
    return f.apply(s);
  }
  @Test
  public void test03(){
    System.out.println(handleStr("abc",(String s)->s.toUpperCase()));
  }
  //結果:ABC

4.Predicate:斷言型接口 boolean test(T t)

  /**
   *  自定義條件過濾字符串集合
   */
  @Test
  public void test04(){
    List<String> strings = Arrays.asList("啊啊啊", "2333", "666", "?????????");
    List<String> stringList = filterStr(strings, (s) -> s.length() > 3);
    for (String s : stringList) {
      System.out.println(s);
    }
  }
  public List<String> filterStr(List<String> list, Predicate<String> predicate){
    ArrayList result = new ArrayList();
    for (int i = 0; i < list.size(); i++) {
      if (predicate.test(list.get(i))){
        result.add(list.get(i));
      }
    }
    return result;
  }

其他接口的定義,如圖所示:
在這裏插入圖片描述

五、方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,就可以使用方法引用!(實現抽象方法的參數列表,必須與方法引用的參數列表)。方法引用可以理解爲方法引用是Lambda表達式的另外一種表現形式。
方法引用的語法:使用操作符“::”將對象或類和方法名分隔開。
方法引用的使用情況共分爲以下三種:

  • 對象::實例方法名
  • 類::靜態方法名
  • 類::實例方法名

使用示例:
1.對象::實例方法名

  /**
   *PrintStream中的println方法定義 
   *     public void println(String x) {
   *         synchronized (this) {
   *             print(x);
   *             newLine();
   *         }
   *     }
   */
  //對象::實例方法名
  @Test
  public void test1(){
    PrintStream out = System.out;
    Consumer<String> consumer=out::println;
    consumer.accept("hello");
  }
  1. 類::靜態方法名
    /**
     * Integer類中的靜態方法compare的定義:
     *     public static int compare(int x, int y) {
     *         return (x < y) ? -1 : ((x == y) ? 0 : 1);
     *     }
     */
  @Test
  public void test2(){
    Comparator<Integer> comparable=(x,y)->Integer.compare(x,y);
    //使用方法引用實現相同效果
    Comparator<Integer> integerComparable=Integer::compare;
    System.out.println(integerComparable.compare(4,2));//結果:1
    System.out.println(comparable.compare(4,2));//結果:1
  }

3.類::實例方法名

  @Test
  public void test3(){
    BiPredicate<String,String> bp=(x,y)->x.equals(y);
    //使用方法引用實現相同效果
    BiPredicate<String,String> bp2=String::equals;
    System.out.println(bp.test("1","2"));//結果:false
    System.out.println(bp.test("1","2"));//結果:false
  }

六、構造器引用與數組引用

1.構造器引用

格式:類名::new
與函數式接口相結合,自動與函數式接口中方法兼容,可以把構造器引用賦值給定義的方法。需要注意構造器參數列表要與接口中抽象方法的參數列表一致。
使用示例:
創建一個實體類Employee:

public class Employee {
  private Integer id;
  private String name;
  private Integer age;

  @Override
  public String toString() {
    return "Employee{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            '}';
  }
  public Employee(){

  }

  public Employee(Integer id) {
    this.id = id;
  }

  public Employee(Integer id, Integer age) {
    this.id = id;
    this.age = age;
  }

  public Employee(int id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
  }
}

使用構造器引用與函數式接口相結合

  @Test
  public void test01(){
    //引用無參構造器
    Supplier<Employee> supplier=Employee::new;
    System.out.println(supplier.get());
    //引用有參構造器
    Function<Integer,Employee> function=Employee::new;
    System.out.println(function.apply(21));
    BiFunction<Integer,Integer,Employee> biFunction=Employee::new;
    System.out.println(biFunction.apply(8,24));
  }
  
輸出結果:
  Employee{id=null, name='null', age=null}
  Employee{id=21, name='null', age=null}
  Employee{id=8, name='null', age=24}

2.數組引用

數組引用的格式:type[]:new
使用示例:

  @Test
  public void test02(){
    Function<Integer,String[]> function=String[]::new;
    String[] apply = function.apply(10);
    System.out.println(apply.length);//結果:10
  }

筆記總結自:尚硅谷的視頻教程-【Java8新特性】
參考:
1.Java 8 Lambda 表達式

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