《Java 8 实战》(二)—— Lambda
Lambda表達(dá)式可以理解為簡潔地表示可傳遞的匿名函數(shù)的一種方式:它沒有名稱,但它有參數(shù)列表/函數(shù)主體/返回類型,可能還有一個可以拋出的異常列表。
?
Lambda表達(dá)式由參數(shù)/箭頭和主體組成:
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
?
之前的代碼形式:
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
}
?
Java 8 中有效的Lambda表達(dá)式:
1, ?(String s) -> s.length()
具有一個String類型的參數(shù),并返回一個int。Lambda表達(dá)式?jīng)]有return語句,因為已經(jīng)隱含了return。
2, ?(Apple a) -> a.getWeight() > 150
參數(shù)為Apple類型,返回一個boolean類型。
3, ?(int x, int y) -> {
System.out.println("Result");
System.out.println(x+y);
}
該Lambda表達(dá)式具有兩個int類型的參數(shù)而沒有返回值。Lambda表達(dá)式可以包含多行語句。
() -> {return "Mario";}
該Lambda表達(dá)式也是有效的。
4, () - > 42
該Lambda表達(dá)式?jīng)]有參數(shù),返回一個int
5, (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
該表達(dá)式具有兩個Apple類型的參數(shù),返回一個int
?
無效Lambda表達(dá)式:
(Integer i) -> return "Alan" + i;
(String s) -> {"IronMan";}
第一個表達(dá)式中,return是一個控制流語句。要使得這個語句有效,需要加上花括號。
第二個表達(dá)式中,"IronMan" 是一個表達(dá)式,不是語句。要使得此Lambda有效,可以去除花括號和分號。
?
Lambda基本語法:
(parameters) -> expression
or
(parameters) -> { statements;}
?
可以使用Lambda表達(dá)式的地方 ——?函數(shù)式接口
只有在接受函數(shù)式接口的地方才可以使用Lambda表達(dá)式。
函數(shù)式接口就是只定義一個抽象方法的接口。如Comparator和Runnable接口。
Lambda表達(dá)式可以直接以內(nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實現(xiàn),并把整個表達(dá)式作為函數(shù)式接口的實例。
函數(shù)式接口的抽象方法的簽名就是Lambda表達(dá)式的簽名。這種抽象方法叫做函數(shù)描述符。
新的Java API中,函數(shù)式接口帶有@FunctionalInterface的標(biāo)注。如果用@FunctionalInterface定義了一個接口,但它卻不是函數(shù)式接口的話,便一起將返回一個提示原因的錯誤,例如“Multiple non-overriding abstract methods found in interface Foo”,表明存在多個抽象方法。
?
使用Lambda表達(dá)式的步驟:
1,行為參數(shù)化:
提取Lambda表達(dá)式,設(shè)計好參數(shù),函數(shù)主體和返回值。
2,使用函數(shù)式接口來傳遞行為:
創(chuàng)建一個能匹配Lambda表達(dá)式的函數(shù)式接口I,并把這個接口作為參數(shù)傳遞給需要使用Lambda表達(dá)式的函數(shù)M。
3,執(zhí)行函數(shù)式接口中的行為
在函數(shù)M中調(diào)用接口I中的抽象函數(shù)
4,傳遞Lambda表達(dá)式
?
常用函數(shù)式接口:
Java 8 以前已有的函數(shù)式接口:
Comparable
Runnable
Callable
Java 8 在java.util.function包中引入的新的函數(shù)式接口:
Predicate
@FunctionalInterface public interface Predicate<T>{boolean test(T t); } public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();for(T s: list){if(p.test(s)){results.add(s);}}return results; } Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
Consumer
@FunctionalInterface public interface Consumer<T>{void accept(T t); } public static <T> void forEach(List<T> list, Consumer<T> c){ for(T i: list){c.accept(i);} } forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
Function
@FunctionalInterface public interface Function<T, R>{R apply(T t); } public static <T, R> List<R> map(List<T> list,Function<T, R> f) {List<R> result = new ArrayList<>();for(T s: list){result.add(f.apply(s));}return result; }List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) -> s.length());
?
?
Lambda及函數(shù)式接口例子
| 使用案例? | Lambda的例子? | 對應(yīng)的函數(shù)式接口 |
| 布爾表達(dá)式? | (List<String> list) -> list.isEmpty()? | Predicate<List<String>> |
| 創(chuàng)建對象? | () -> new Apple(10) | Supplier<Apple> |
| 消費一個對象 | (Apple a) -> System.out.println(a.getWeight()) | ? Consumer<Apple> |
| ?從一個對象中選擇/提取 | ?(String s) -> s.length() | ? Function<String, Integer>或 |
| ?合并兩個值 | ?(int a, int b) -> a * b | ?IntBinaryOperator |
| 比較兩個對象? | ? (Apple a1, Apple a2) -> | ? Comparator<Apple>或 |
?
函數(shù)式接口異常:
任何函數(shù)式接口都不允許拋出受檢異常(checked Exception。 如果需要Lambda表達(dá)式拋出異常,有兩種方式:
1,定義一個自己的函數(shù)式接口,并聲明受檢異常:?
@FunctionalInterface public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException; } BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();
2,把Lambda包在一個try/cache塊中。
Function<BufferedReader, String> f = (BufferedReader b) -> {try {return b.readLine();}catch(IOException e) {throw new RuntimeException(e);} };
?
Lambda表達(dá)式類型檢查:
Lambda的類型是從使用Lambda的上下文推斷出來的。上下文中Lambda表達(dá)式需要的類型成為目標(biāo)類型。例如:?
List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);
類型檢查過程:
1,找出filter方法的聲明
2,要求它是predicate<Apple>對象的第二個正式參數(shù)。
3,Predicate<Apple>是一個函數(shù)式接口,定義了一個叫做test的抽象方法。
4,test方法描述了一個函數(shù)描述符,它可以接受一個Apple,并返回一個boolean.
5, filter的任何實際參數(shù)都匹配這個要求。
?
Lambda表達(dá)式類型推斷:
編譯器可以了解Lambda表達(dá)式的參數(shù)類型,這樣可以在Lambda語法中省去標(biāo)注參數(shù)類型,即:
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));
但有時顯式寫出類型更易讀,有時候去掉更易讀,需要自己權(quán)衡。
當(dāng)Lambda僅有一個類型需要推斷的參數(shù)時,參數(shù)名稱兩邊的括號也可以省略。
?
方法引用:
參考:http://www.cnblogs.com/chenpi/p/5885706.html
什么是方法引用
簡單地說,就是一個Lambda表達(dá)式。在Java 8中,我們會使用Lambda表達(dá)式創(chuàng)建匿名方法,但是有時候,我們的Lambda表達(dá)式可能僅僅調(diào)用一個已存在的方法,而不做任何其它事,對于這種情況,通過一個方法名字來引用這個已存在的方法會更加清晰,Java 8的方法引用允許我們這樣做。方法引用是一個更加緊湊,易讀的Lambda表達(dá)式,注意方法引用是一個Lambda表達(dá)式,其中方法引用的操作符是雙冒號"::"。
方法引用例子
?先看一個例子
首先定義一個Person類,如下:
package methodreferences;import java.time.LocalDate;public class Person
{public Person(String name, LocalDate birthday){this.name = name;this.birthday = birthday;}String name;LocalDate birthday;public LocalDate getBirthday(){return birthday;}public static int compareByAge(Person a, Person b){return a.birthday.compareTo(b.birthday);}@Overridepublic String toString(){return this.name;}
} 假設(shè)我們有一個Person數(shù)組,并且想對它進(jìn)行排序,這時候,我們可能會這樣寫:
原始寫法
package methodreferences;import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;public class Main
{static class PersonAgeComparator implements Comparator<Person> {public int compare(Person a, Person b) {return a.getBirthday().compareTo(b.getBirthday());}}public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, new PersonAgeComparator());System.out.println(Arrays.asList(pArr));}
} 其中,Arrays類的sort方法定義如下:
public static <T> void sort(T[] a, Comparator<? super T> c)
這里,我們首先要注意Comparator接口是一個函數(shù)式接口,因此我們可以使用Lambda表達(dá)式,而不需要定義一個實現(xiàn)Comparator接口的類,并創(chuàng)建它的實例對象,傳給sort方法。
使用Lambda表達(dá)式,我們可以這樣寫:
改進(jìn)一,使用Lambda表達(dá)式,未調(diào)用已存在的方法
package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, (Person a, Person b) -> {return a.getBirthday().compareTo(b.getBirthday());});System.out.println(Arrays.asList(pArr));}
} 然而,在以上代碼中,關(guān)于兩個人生日的比較方法在Person類中已經(jīng)定義了,因此,我們可以直接使用已存在的Person.compareByAge方法。
改進(jìn)二,使用Lambda表達(dá)式,調(diào)用已存在的方法
package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, (a, b) -> Person.compareByAge(a, b));System.out.println(Arrays.asList(pArr));}
} 因為這個Lambda表達(dá)式調(diào)用了一個已存在的方法,因此,我們可以直接使用方法引用來替代這個Lambda表達(dá)式,
改進(jìn)三,使用方法引用
package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, Person::compareByAge);System.out.println(Arrays.asList(pArr));}
} 在以上代碼中,方法引用Person::compareByAge在語義上與Lambda表達(dá)式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:
- 真實的參數(shù)是拷貝自Comparator<Person>.compare方法,即(Person, Person);
- 表達(dá)式體調(diào)用Person.compareByAge方法;
四種方法引用類型
靜態(tài)方法引用
我們前面舉的例子Person::compareByAge就是一個靜態(tài)方法引用。
特定實例對象的方法引用
如下示例,引用的方法是myComparisonProvider?對象的compareByName方法;
class ComparisonProvider{public int compareByName(Person a, Person b){return a.getName().compareTo(b.getName());}public int compareByAge(Person a, Person b){return a.getBirthday().compareTo(b.getBirthday());}}ComparisonProvider myComparisonProvider = new ComparisonProvider();Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 任意對象(屬于同一個類)的實例方法引用
如下示例,這里引用的是字符串?dāng)?shù)組中任意一個對象的compareToIgnoreCase方法。
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase); 構(gòu)造方法引用
如下示例,這里使用了關(guān)鍵字new,創(chuàng)建了一個包含Person元素的集合。
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
transferElements方法的定義如下,功能為集合拷貝,
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) {DEST result = collectionFactory.get();for (T t : sourceCollection) {result.add(t);}return result;
} 什么場景適合使用方法引用
當(dāng)一個Lambda表達(dá)式調(diào)用了一個已存在的方法
什么場景不適合使用方法引用
當(dāng)我們需要往引用的方法傳其它參數(shù)的時候,不適合,如下示例:
IsReferable demo = () -> ReferenceDemo.commonMethod("Argument in method."); ?
?
Lambda 和 方法引用實戰(zhàn)
第一步,傳遞代碼
public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2) {return a1.getWeight().compareTo(a2.getWeight());} }inventory.sort(new AppleComparator());
第二步,使用匿名類
inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2) {return a1.getWeight().compareTo(a2.getWeight());} })
第三步,使用Lambda表達(dá)式
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
Java編譯器可以根據(jù)Lambda出現(xiàn)的上下文來推斷Lambda表達(dá)式參數(shù)的類型,所以可以改寫為:
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
Comparator具有一個叫做comparing的靜態(tài)方法,可以接受一個Function來提取Comparable鍵值,并生成一個Comparator對象,如下:
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
所以再次改寫:
inventory.sort(comparing((a) -> a.getWeight()));
第四步,使用方法引用
inventory.sort(comparing(Apple::getWeight));
?
復(fù)合Lambda表達(dá)式
1,比較器復(fù)合
a,逆序
inventory.sort(comparing(Apple::getWeight).reversed())
b, 比較器鏈
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
2, 謂詞復(fù)合
a,negate
Predicate<Apple> notRedApple = redApple.negate();
b, and
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
c, or
Predicate<Apple> redAAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));
3, 函數(shù)復(fù)合
a,andThen 相當(dāng)于g(f(x))
Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.andThen(g); int result = h.apply(1); // result = 4
b, compose 相當(dāng)于f(g(x))
Function<Integer, Integer> f = x -> x + 1; Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h = f.compose(g); int result = h.apply(1); // result = 3
?
轉(zhuǎn)載于:https://www.cnblogs.com/IvySue/p/6737133.html
總結(jié)
以上是生活随笔為你收集整理的《Java 8 实战》(二)—— Lambda的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喂你们八卦的版主能不能不要老是搞一些转眼
- 下一篇: 《长相思·九月西风兴》第二十句是什么