Java8函數(shù)式接口和Lambda表達式,你真的會了嗎
本文轉載自微信公眾號「愛笑的架構師」,作者雷架 。轉載本文請聯(lián)系愛笑的架構師 公眾號。
Java8 由Oracle在2014年發(fā)布,是繼Java5之后最具革命性的版本了。Java8吸收其他語言的精髓帶來了函數(shù)式編程,lambda表達式,Stream流等一系列新特性,學會了這些新特性,可以讓你實現(xiàn)高效編碼優(yōu)雅編碼。
Lambda表達式小試牛刀
Lambada表達式可以理解為:可傳遞的匿名函數(shù)的一種簡潔表達方式。Lambda表達式?jīng)]有名稱,同普通方法一樣有參數(shù)列表、函數(shù)主體、返回類型等;
下面簡單看一個例子,new一個線程打印字符串,采用lambda表達式非常簡潔:
- new Thread(() -> System.out.println("hello java8 lambda")).start()
Thread類接受一個Runnable類型實例,查看Jdk源碼發(fā)現(xiàn)Runnable接口是一個函數(shù)式接口,可以直接用lambda表達式替代。
- @FunctionalInterface
- public interface Runnable {
- public abstract void run();
- }
Lambda表達式語法非常簡單:
- () -> System.out.println("hello java8 lambda")
- ()括號里面是參數(shù)列表,如果只有一個參數(shù)還可以寫為:a -> System.out.println(a)
- -> 箭頭為固定寫法;
- System.out.println("hello java8 lambda") 為函數(shù)主體,如果有多條語句要用花括號包裹起來, 比如下面這樣:
- (a, b) -> {int sum = a + b; return sum;}
綜上,Lambda表達式模塊可以固化為:
- (parameter) -> {expression} 或者 (parameter) -> {statements; statements; }
數(shù)只有一個可以省略括號
如果不用Lambda表達式,使用匿名內(nèi)部類的方式,寫法就不是那么優(yōu)雅了。
- // before Java8
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("hello java8 without lambda");
- }
- }).start();
Lambda高階用法
(1)函數(shù)式接口
函數(shù)式接口是只定義了一個抽象方法的接口。注意Java8中允許存在默認方法(default),哪怕有很多默認方法,只要有且僅有一個抽象方法,那么這個接口仍然是函數(shù)式接口。
函數(shù)式接口通常在類上有一個注解@FunctionalInterface,如:
- @FunctionalInterface
- public interface Runnable {
- public abstract void run();
- }
(2)函數(shù)式接口可以干什么?
通常lambda表達式與函數(shù)式接口結合一起用,lambda表達式以內(nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實現(xiàn),把整個表達式作為函數(shù)式接口的實例。在沒有l(wèi)ambda表達式之前,我們通常會使用匿名內(nèi)部類的方式實現(xiàn),詳細對比見第一小節(jié)的實例代碼。
(3)函數(shù)描述符
函數(shù)式接口抽象方法的簽名基本上就是lambda表達式的簽名,我們可以將這種對應關系稱為函數(shù)描述符。由一個函數(shù)式接口的抽象方法抽象為一個函數(shù)描述符,這個過程非常重要,知道了函數(shù)描述符去寫lambda表達式也就非常容易了。舉個例子:
Runnable接口有一個抽象方法 void run(), 接受空參數(shù)返回void,那么函數(shù)描述符可以推導為:
- () -> void
- lambda表達式可以寫為
- () -> System.out.println("hello java8 lambda")
(4)常用函數(shù)式接口
java8 中常用函數(shù)式接口,針對基本類型java還定義了IntPredicate, LongPredicate等類型,詳細可以參考jdk源碼。
函數(shù)式接口 | 函數(shù)描述符 |
---|---|
Predicate | T->boolean |
Consumer | T->void |
Function<T,R> | T->R |
Supplier | () -> T |
UnaryOperator | T -> T |
BinaryOperator | (T,T)->T |
BiPredicate<L,R> | (L,R)->boolean |
BiConsumer<T,U> | (T,U)->void |
BiFunction<T,U,R> | (T,U)->R |
至于 Predicate, Consumer, Function這些函數(shù)式接口具體作用,在后面的文章中會詳細介紹,這里只需有個大體印象即可。
(5)將lambda表達式重構為方法引用
方法引用可以看作是lambda表達式的一種快捷寫法,它可以調用特性的方法作為參數(shù)傳遞。你也可以將方法引用看作是lambda表達式的語法糖,讓lambda表達式寫起來更加簡介。舉個栗子,按學生年齡排序:
- // before
- students.sort((s1, s2) -> s1.getAge.compareTo(s2.getAge()))));
- // after 使用方法引用
- students.sort(Comparator.comparing(Student::getAge()))));
方法引用主要有三類:
- 靜態(tài)方法的方法引用
valueOf是String類的靜態(tài)方法,方法引用寫為 String::valueOf, 對應lambda表達式:a -> String.valueOf(a)
- 任意類型實例方法的方法引用
length是String類的實例方法,方法引用寫為 String::length,對應lambda表達式:(str) -> str.length()
- 現(xiàn)有對象的實例方法的方法引用
第三種容易與第二種混淆,現(xiàn)有對象指的是在lambda表達式中調用外部對象(不是入?yún)ο?的實例方法,比如:
- String str = "hello java8";
- () -> str.length();
對應方法引用寫為 str::length, 注意不是 String::length
最后我們將三類方法引用歸納如下:
lambda表達式 | 方法引用 | |
---|---|---|
(args) -> ClassName.staticMethod(args) | ClassName::staticMethod | 靜態(tài)方法方法引用 |
(arg0, params) -> arg0.instanceMethod(params) | ClassName::instanceMethod | 內(nèi)部實例方法引用 |
arg0 (params) -> arg0.instanceMethod(params) |
arg0.instanceMethod | 外部實例方法引用 |
總結:通過這篇文章的學習應該是對java8函數(shù)式編程和lambda表達式有了一個初步的了解,后面會有真實項目實戰(zhàn)演練,敬請期待吧~