1. 程式人生 > 程式設計 >java 8 lambda

java 8 lambda

java 8 stream 入門

lambda

在學習lambda之前先來看一段程式碼,傳入一個User的集合,返回符合條件的User集合

  public static List<User> filter(List<User> users,Predicate<User> predicate) {
    List<User> result = new ArrayList<>();
    for (User user: users){
      if (predicate.test(user)) result.add(user);
    }
    return
result; } 複製程式碼

程式碼中使用了Predicate介面,接受一個泛型T,返回一個布林值

  @FunctionalInterface
  public interface Predicate<T> {
    boolean test(T t);
  }
複製程式碼

java7中,我們可以使用匿名內部類來作為入參來呼叫filter方法

  public static List<User> filterZhang(List<User> users) {
    return filter(users,new Predicate<User>() {
      @Override
public boolean test(User user) { return user.getName().startsWith("張"); } }); } 複製程式碼

java8中,我們可以使用lambda來實現

  public static List<User> filterWang(List<User> users) {
    return filter(users,user -> user.getName().startsWith("王"));
  }
複製程式碼

lambda其實是一個箭頭函式,也可稱為匿名函式

,類似於ES6中的箭頭函式,只不過javascript中使用=>,而java中使用->

lambda的語法

箭頭操作符將lambda表示式分成了兩部分:

  • 左側:lambda表示式的引數列表(介面中抽象方法的引數列表)
  • 右側:lambda表示式中所需執行的功能(lambda體,對抽象方法的實現)

上面說到的抽象方法,指的就是Predicate介面中唯一的一個抽象方法 test,接收一個泛型T,返回boolean值

boolean test(T t)
複製程式碼

再看看我們寫的lambda表示式,是不是接受一個泛型為User的user物件,如果user的姓王就返回true,反之返回false,引數和返回值 一一對應

user -> user.getName().startsWith("王")
複製程式碼

所以 lambda箭頭函式必須和Predicate介面中那個唯一的抽象方法保持一致(引數和返回值完全相同),正因為如此,lambda中會對引數型別進行型別推斷, 我們只寫了一個user,java就知道這是一個User物件

語法有如下幾種格式:

  • 語法格式一(無引數無返回值):
() -> 具體實現
複製程式碼
  • 語法格式二(有一個引數無返回值):
(x) -> 具體實現 
//或 
x -> 具體實現
複製程式碼
  • 語法格式三(有多個引數,有返回值,並且lambda體中有多條語句):
(x,y) -> {具體實現}
複製程式碼
  • 語法格式四:若方法體只有一條語句,那麼大括號和return都可以省略

注:lambda表示式的引數列表的引數型別可以省略不寫,可以進行型別推斷

函式式介面

什麼是函式式介面?

  • 像Runnable和Comparator這樣只有一個抽象方法的介面,稱為函式式介面。 也可以在介面上加上@FunctionalInterface註解,如果編譯通過,則該介面就是函式式介面。
  • lambda表示式就是對函式式介面中那個唯一的抽象方法的實現

函式是介面都有哪些?

函式式介面大部分定義在 java.util.function包中, 且用@FunctionalInterface註解

看一個需求

需求:需要對兩個數進行加減乘除等運算,怎麼實現?

  • 傳統做法:傳統做法中,需要進行幾種運算,我們就要寫幾個方法。一種運算對應一個方法。
public static void main(String[] args) {
    add(2,1);
    minus(2,1);
    multiply(2,1);
    divide(2,1);
  }
  
  static int add(int left,int right) {
    return left + right;
  }

  static int minus(int left,int right) {
    return left - right;
  }
  static int multiply(int left,int right) {
    return left * right;
  }

  static int divide(int left,int right) {
    return left / right;
  }
複製程式碼
  • lambda做法:首先要定義一個函式式介面,介面中只有一個方法,接收兩個引數。
public static void main(String[] args) {
    calc(2,1,(left,right) -> left + right);
    calc(2,right) -> left - right);
    calc(2,right) -> left * right);
    calc(2,right) -> left / right);
  }

  static int calc(int left,int right,Calculate calculate) {
    return calculate.applyAsInt(left,right);
  }

  @FunctionalInterface
  interface Calculate {
    int applyAsInt(int left,int right);
  }
d
複製程式碼

所以用lambda的話,只需要定義一個函式式介面,不管進行什麼操作,都可以用lambda解決,不用再一種運算對應一個方法。但是,還需要自己定義函式式介面,好像也沒簡單很多。Java考慮到這點了,所以內建了函式式介面,大部分放在java.util.function包中,介面用@FunctionalInterface註解。

如Predicate介面

方法引用

  • 當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用。
  • 如果某個方法和 函式式介面中那個唯一的抽象函式保持一致(引數和返回值), 則可以使用雙引號 :: 的方式
  • 不過實現抽象方法的引數列表,必須與引用方法的引數列表保持一致。

方法引用語法:

  • 物件::例項方法
  • 類::靜態方法
  • 類::例項方法

在將lambda時舉的例子,可以換成方法引用的寫法

  public static List<User> filterLi(List<User> users) {
    return filter(users,Demo1::test);
  }
  // test 相當於 Predicat介面中抽象方法test的實現
  private static boolean test(User user) {
    return user.getName().startsWith("李");
  }
複製程式碼

方法引用相較於lambda表示式的優點

  • 有自己的方法名,要幹什麼一目瞭然
  • 因為是一個方法,所以可以有複雜的邏輯程式碼,而在lambda中寫複雜的邏輯程式碼是很不優雅的,可讀性變差