java 8 lambda

java 8 stream 入門javascript

lambda

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

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,返回一個布爾值es6

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

java7中,咱們可使用匿名內部類來做爲入參來調用filter方法bash

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來實現app

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

lambda實際上是一個箭頭函數,也可稱爲匿名函數,相似於ES6中的箭頭函數,只不過javascript中使用=>, 而java中使用->ide

lambda的語法

箭頭操做符將lambda表達式分紅了兩部分:函數

  • 左側:lambda表達式的參數列表(接口中抽象方法的參數列表)
  • 右側:lambda表達式中所需執行的功能(lambda體,對抽象方法的實現)

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

boolean test(T t)
複製代碼

再看看咱們寫的lambda表達式, 是否是接受一個泛型爲User的user對象,若是user的姓王就返回true,反之返回false, 參數和返回值 一一對應學習

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

因此 lambda箭頭函數必須和Predicate接口中那個惟一的抽象方法保持一致(參數和返回值徹底相同),正由於如此,lambda中會對參數類型進行類型推斷, 咱們只寫了一個user, java就知道這是一個User對象ui

語法有以下幾種格式:

  • 語法格式一(無參數無返回值):
() -> 具體實現
複製代碼
  • 語法格式二(有一個參數無返回值):
(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, 1, (left, right) -> left - right);
    calc(2, 1, (left, right) -> left * right);
    calc(2, 1, (left, 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中寫複雜的邏輯代碼是很不優雅的, 可讀性變差
相關文章
相關標籤/搜索