Java8應該是目前最大的一次更新了,更新後咱們迎來了不少新特性,其中便包括Lambda表達式,函數式編程的思想正式進入Java,讓咱們看一個經典案例。java
採用匿名內部類已經算簡介了,若是專門用一個類去實現Comparator再new出來就更煩了,過去的寫法:express
// 已經建立好了三個Person實例 List<Person> people = Arrays.asList(person1, person2, person3); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge().compareTo(o2.getAge()); } });
Lambda版本寫法:編程
Collections.sort(people, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
還有更簡潔的方法引用寫法:設計模式
Collections.sort(people, Comparator.comparing(Person::getAge));
是否是真的短真的易讀,語法糖真的甜!已經9102年了,函數式編程被提到的愈來愈多,深諳照貓畫虎已經行不通了,並且函數式編程和設計模式的碰撞也不少,真的有必要了解下相關概念app
函數式編程是一種思想,核心是行爲參數化,把一段代碼像值同樣傳遞給方法,傳入不一樣的代碼實現不一樣的功能ide
這是否是很像策略模式以及模板模式?如例1所示,不須要大量的套路代碼了,也不須要把代碼寫到一個類中而後新建實例對象最後把實例對象傳遞函數式編程
函數式接口就是隻定義一個抽象方法的接口來表示行爲,抽象方法不容許拋出受檢異常,Java8接口能夠有default方法了,函數式接口是容許有default方法的函數
Lambda表達式看上去確實頗有吸引力,我能在任何地方都使用麼?答案是不能的,咱們只能經過Lambda表達式把代碼傳到函數式接口中,拿例1中的Comparator接口來看設計
@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); }
Comparator接口只有compare一個抽象方法,Java8特地給它加了註解告訴咱們這就是個函數式接口,其實也很好理解,咱們沒有建立任何實例,只傳了一串代碼,若是Comparator有兩個抽象方法,編譯器怎麼知道咱們實現的是compare方法呢code
上面都是說的函數式編程,那麼什麼是Lambda
Lambda就是匿名的行爲參數化的一種語法實現,它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個能夠拋出的異常列表
tips:若是statements很長,那麼咱們就不該該用Lambda,而應該單獨實現一個方法,而後使用方法引用這樣可讀性更好,繼續例1
// 好比說在MyUtils類下寫個方法,故意加長 public static Integer sortPersonByName(Person person1, Person person2) { Integer age1 = person1.getAge(); Integer age2 = person2.getAge(); return age1.compareTo(age2); } // 又用到了方法引用,咱們能夠把方法引用看成一種便於閱讀的語法糖,功能也是傳遞代碼 Collections.sort(people, MyUtils::sortPersonByAge);
在使用Lambda的時候咱們沒有任何類型聲明就能工做這是怎麼作到的呢?
函數式接口的抽象方法的簽名基本上就是Lambda表達式的簽名。咱們將這種抽象方法叫做
函數描述符
Comparator.compare的簽名
int compare(T o1, T o2);
這個函數式接口的簽名就能夠描述成須要兩個相同類型的變量,而後返回int
( T, T ) -> int
Lambda的簽名
(p1, p2) -> p1.getAge().compareTo(p2.getAge()) // 這是易讀的寫法,咱們也能夠寫成方便說明 (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge())
能夠描述成須要兩個Person變量,而後compareTo方法返回int
(Person, Person) -> int
編譯器會作類型推斷和類型檢查,發現兩個簽名匹配,咱們的Lambda表達式就能夠順利執行了
tips:Lambda主體是語句表達式的時候(parameters) -> expression ,儘管expression返回可能不是void,可是也是兼容 T -> void 簽名的
除了 Runnable,Comparator等經常使用函數式接口,爲了推進函數式編程,Java8又在 java.util.function 包下爲咱們提供了大量好用的函數式,由於基本數據類型不能抽象成對象,因此能夠看到有大量Double,Int,Long前綴的接口,咱們只看比較核心的:
接口名 | 抽象方法 | 描述符 |
---|---|---|
Predicate
|
boolean test(T t) | T -> boolean |
BiPredicate<T, U> | test(T t, U u) | ( T, U ) -> boolean |
Consumer
|
void accept(T t) | T -> void |
BiConsumer<T, U> | void accept(T t, U u) | ( T, U ) -> void |
Function<T, R> | R apply(T t) | T -> R |
BiFunction<T, U, R> | R apply(T t, U u) | ( T, U ) -> R |
Supplier
|
T get() | void -> T |
tips:Predicate這種對給定內容作判斷返回boolean 值,咱們叫作謂詞
使人驚喜的是,java8提供的函數式接口還有許多好用的default方法,可讓咱們把多個Lambda複合起來,組成流水線。拿用的比較多的Function接口舉例,咱們要寫一封郵件,咱們關注的是信的內容
Function<String, String> writeEmailHeader = text -> "Hi ," + "\n" + text; Function<String, String> writeEmailText = text -> text + "\n"; Function<String, String> writeEmailFooter = text -> text + "BRs" + "\n" + "Chen"; Function<String, String> writeEmail = writeEmailHeader.andThen(writeEmailText).andThen(writeEmailFooter); System.out.println(writeEmail.apply("I will take half day sick leave today")); /** * 輸入以下: * Hi , * I will take half day sick leave today * BRs * Chen */