Java 8 lambda運算式

來源:互聯網
上載者:User

標籤:iterable   static   pareto   參數   zab   儲存   log   編程   require   

Lambda運算式簡介


  lambda運算式可以被理解為一個匿名函數的簡明表示,它可以被傳遞,它沒有名稱,但是它有一個參數列表,一個主體,一個傳回型別,也可能是可以拋出的異常列表。

  • 匿名 因為它沒有一個明確的名字,不像一個方法,必須有方法名。
  • 函數 因為lambda不是屬於某一個特定類的方法,但它和方法一樣,lambda有一個參數列表,一個主體,一個傳回型別,以及可能拋出的異常。
  • 傳遞 一個lambda運算式可以作為參數傳遞給一個方法或儲存在一個變數。
  • 簡潔 你不需要寫很多冗長的代碼,就是比寫匿名內部類還簡潔。

在使用Java 8以前,建立一個比較子,一般都會採用內部類方式:

Comparator<Apple> byWeight = new Comparator<Apple>() {    public int compare(Apple a1, Apple a2) {        return a1.getWeight().compareTo(a2.getWeight());    }};

使用lambda運算式之後的代碼,只需一行代碼。

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

 

lambda運算式的文法結構

  lambda運算式由參數、箭頭、主體,這三部分構成。

  • 參數 跟一個方法的參數類似。
  • 箭頭 由參數列表指向函數主體。
  • 主體 函數的具體運算操作。
lambda運算式樣本
  • (parameters) -> expression 是否有傳回值取決於後面expression的傳回值。
  • (parameters) -> { statements; } 多個語句需要用{}包含。
  • (parameters) -> { statements;return 23 } 用{}包含的語句,如果有傳回值,需要顯示return指定。

正確的運算式

(String s) -> s.length() 這個運算式有一個String的參數s,返回int,lambda運算式隱藏的return返回語句;
(Apple a) -> a.getWeight() > 150 這個運算式有一個參數Apple,返回boolean
(int x,int y) -> {System.out.println(“result:”),System.out.println(x+y)} 這個運算式有兩個int型參數,沒有傳回值(或返回void) ,lambda運算式可以包含多個語句,但必須用{}大括弧包含在裡面。
() -> 42 這個運算式沒有參數,但返回int型

錯誤的運算式

(int i) -> return “result:” + i; 正確寫法(int i) -> {return “result:” + i;}
(String s) -> {“zhang” + s;} 正確寫法(String s) -> {return “zhang” + s;}或者(String s) -> “zhang” + s

精簡寫法

參數類型可以省略,例如:Comparator<apple> c = (a1,a2) -> a1.getWeight().compareTo(a2.getWeight());
當一個lambda運算式只有一個參數時,參數類型可以省略,括弧也可以省略。
例如List<Apple> greenApples = filter(inventory,a - > "green".eqauls(a.getColor()));

函數式介面


  所謂函數式介面,就是某個介面,它只有一個抽象方法。採用lambda運算式的作用,相當於該介面的唯一一個抽象方法的實現。單一抽象方法的介面,在JDK很常見,常用的有:

public interface Comparator<t> {    int compare(T o1,T o2);}public interface Runnable {    void run();}public interface EventListener {    void actionPerformed(ActionEvent e);}
@FunctionalInterface註解

 

  Java 8 專門為函數式編程,新增了java.util.function包的一些介面類,如果查看這些介面類的源碼,會注意到這些函數式介面都是帶@FunctionalInterface註解,此註解用於指示該介面為函數式介面。如果你定義了一個介面加上了@FunctionalInterface註解,但是介面裡有多個抽象方法,這時候編譯器在編譯的時候就會報錯。

Java 8裡的函數式介面

  Java 8已經提供大量的函數式介面,在寫lambda運算式時,經常會碰到,這運算式要怎麼寫,就要先瞭解對應的函數式介面裡方法的參數和傳回值。

Predicate
@FunctionalInterfacepublic interface Predicate<T> {    boolean test(T t);}

Predicate介面裡的test方法,參數是T,返回boolean型,使用情境Stream的filter(Predicate<? super T> predicate)

Consumer
@FunctionalInterfacepublic interface Consumer<T> {    void accept(T t);}

Consumer介面的accept方法,參數是T,返回void,使用情境Iterable的forEach(Consumer<? super T> action)

Function
@FunctionalInterfacepublic interface Function<T, R> {    R apply(T t);}

Function介面的apply方法,參數是T,傳回值R,使用情境Stream裡的map(Function<? super T, ? extends R> mapper),flapMap(Function<? super T, ? extends R> mapper)

方法引用


  前面lambda運算式簡介裡,介紹了lambda運算式是一個匿名的函數,可以被當做參數傳遞給調用方。如果傳給調用方的這個參數不再是lambda運算式,而是源自於某個類的方法,這時就稱之為方法引用。
例如用lambda表示比較子:
Arrays.sort(list,(Apple a,Apple b) -> a.getWeight().compareTo(b.getWeight()));
採用方法引用的寫法:
Arrays.sort(list,Comparator.comparing(Apple::getWeight))
開啟Comparator的comparing源碼可以看到,這個方法裡已經實現了的比較子的判斷規則,只不過這個規則是根據傳入的函數(或方法)的傳回值,做大小比較。

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(        Function<? super T, ? extends U> keyExtractor) {    Objects.requireNonNull(keyExtractor);    return (Comparator<T> & Serializable)        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));}
方法引用的三種方式

方法引用靜態方法,例如,Integer的parseInt(),寫成Integer::parseInt。
一個方法引用一個任意類型的執行個體方法,例如String的length((),寫成String::length。
方法引用現有對象的執行個體方法,例如有個本地變數obj,它有個getValue()的方法,寫成obj::getValue

    中需要注意的是第二點,lambda的第一個參數是,是方法調用的執行個體對象,與之對應的方法引用,會排除第一個參數,也就是arg0會忽略掉,只有rest作為參數。
Function<String, Integer> stringToInteger =(String s) -> Integer.parseInt(s);,對應的方法引用是Integer::parseInt
BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);這個lambda運算式裡,用第一個參數的執行個體對象去調用contains方法,屬於方法引用的第二種方式,所以對應的方法引用是 List::contains。這裡主要的混淆點是contains()方法的參數只有一個,但因為這個方法屬於String的執行個體方法(而不是類的靜態方法),隱含了第一個參數。

構造方法引用

  類的構造方法也可以被引用,使用文法是ClassName::new。例如:

Supplier<Apple> c1 = App::new; App a1 = c1.get(()

    這個語句引用的是無參構造方法,也可以引用有參構造方法:

BiFunction<String,Integer,Apple> c3 = Apple::new; App a1 = c3.apply("gree",110)

    這裡的構造方法Apple(String color,Integer weight)有兩個參數。

參考資料
  • 《Java 8 in Action》

Java 8 lambda運算式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.