《Java 8 实战》(二)—— Lambda
Lambda表達式可以理解為簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表/函數主體/返回類型,可能還有一個可以拋出的異常列表。
?
Lambda表達式由參數/箭頭和主體組成:
(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表達式:
1, ?(String s) -> s.length()
具有一個String類型的參數,并返回一個int。Lambda表達式沒有return語句,因為已經隱含了return。
2, ?(Apple a) -> a.getWeight() > 150
參數為Apple類型,返回一個boolean類型。
3, ?(int x, int y) -> {
System.out.println("Result");
System.out.println(x+y);
}
該Lambda表達式具有兩個int類型的參數而沒有返回值。Lambda表達式可以包含多行語句。
() -> {return "Mario";}
該Lambda表達式也是有效的。
4, () - > 42
該Lambda表達式沒有參數,返回一個int
5, (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
該表達式具有兩個Apple類型的參數,返回一個int
?
無效Lambda表達式:
(Integer i) -> return "Alan" + i;
(String s) -> {"IronMan";}
第一個表達式中,return是一個控制流語句。要使得這個語句有效,需要加上花括號。
第二個表達式中,"IronMan" 是一個表達式,不是語句。要使得此Lambda有效,可以去除花括號和分號。
?
Lambda基本語法:
(parameters) -> expression
or
(parameters) -> { statements;}
?
可以使用Lambda表達式的地方 ——?函數式接口
只有在接受函數式接口的地方才可以使用Lambda表達式。
函數式接口就是只定義一個抽象方法的接口。如Comparator和Runnable接口。
Lambda表達式可以直接以內聯的形式為函數式接口的抽象方法提供實現,并把整個表達式作為函數式接口的實例。
函數式接口的抽象方法的簽名就是Lambda表達式的簽名。這種抽象方法叫做函數描述符。
新的Java API中,函數式接口帶有@FunctionalInterface的標注。如果用@FunctionalInterface定義了一個接口,但它卻不是函數式接口的話,便一起將返回一個提示原因的錯誤,例如“Multiple non-overriding abstract methods found in interface Foo”,表明存在多個抽象方法。
?
使用Lambda表達式的步驟:
1,行為參數化:
提取Lambda表達式,設計好參數,函數主體和返回值。
2,使用函數式接口來傳遞行為:
創建一個能匹配Lambda表達式的函數式接口I,并把這個接口作為參數傳遞給需要使用Lambda表達式的函數M。
3,執行函數式接口中的行為
在函數M中調用接口I中的抽象函數
4,傳遞Lambda表達式
?
常用函數式接口:
Java 8 以前已有的函數式接口:
Comparable
Runnable
Callable
Java 8 在java.util.function包中引入的新的函數式接口:
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及函數式接口例子
| 使用案例? | Lambda的例子? | 對應的函數式接口 |
| 布爾表達式? | (List<String> list) -> list.isEmpty()? | Predicate<List<String>> |
| 創建對象? | () -> 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>或 |
?
函數式接口異常:
任何函數式接口都不允許拋出受檢異常(checked Exception。 如果需要Lambda表達式拋出異常,有兩種方式:
1,定義一個自己的函數式接口,并聲明受檢異常:?
@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表達式類型檢查:
Lambda的類型是從使用Lambda的上下文推斷出來的。上下文中Lambda表達式需要的類型成為目標類型。例如:?
List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);
類型檢查過程:
1,找出filter方法的聲明
2,要求它是predicate<Apple>對象的第二個正式參數。
3,Predicate<Apple>是一個函數式接口,定義了一個叫做test的抽象方法。
4,test方法描述了一個函數描述符,它可以接受一個Apple,并返回一個boolean.
5, filter的任何實際參數都匹配這個要求。
?
Lambda表達式類型推斷:
編譯器可以了解Lambda表達式的參數類型,這樣可以在Lambda語法中省去標注參數類型,即:
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));
但有時顯式寫出類型更易讀,有時候去掉更易讀,需要自己權衡。
當Lambda僅有一個類型需要推斷的參數時,參數名稱兩邊的括號也可以省略。
?
方法引用:
參考:http://www.cnblogs.com/chenpi/p/5885706.html
什么是方法引用
簡單地說,就是一個Lambda表達式。在Java 8中,我們會使用Lambda表達式創建匿名方法,但是有時候,我們的Lambda表達式可能僅僅調用一個已存在的方法,而不做任何其它事,對于這種情況,通過一個方法名字來引用這個已存在的方法會更加清晰,Java 8的方法引用允許我們這樣做。方法引用是一個更加緊湊,易讀的Lambda表達式,注意方法引用是一個Lambda表達式,其中方法引用的操作符是雙冒號"::"。
方法引用例子
?先看一個例子
首先定義一個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;}
} 假設我們有一個Person數組,并且想對它進行排序,這時候,我們可能會這樣寫:
原始寫法
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接口是一個函數式接口,因此我們可以使用Lambda表達式,而不需要定義一個實現Comparator接口的類,并創建它的實例對象,傳給sort方法。
使用Lambda表達式,我們可以這樣寫:
改進一,使用Lambda表達式,未調用已存在的方法
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));}
} 然而,在以上代碼中,關于兩個人生日的比較方法在Person類中已經定義了,因此,我們可以直接使用已存在的Person.compareByAge方法。
改進二,使用Lambda表達式,調用已存在的方法
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表達式調用了一個已存在的方法,因此,我們可以直接使用方法引用來替代這個Lambda表達式,
改進三,使用方法引用
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表達式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:
- 真實的參數是拷貝自Comparator<Person>.compare方法,即(Person, Person);
- 表達式體調用Person.compareByAge方法;
四種方法引用類型
靜態方法引用
我們前面舉的例子Person::compareByAge就是一個靜態方法引用。
特定實例對象的方法引用
如下示例,引用的方法是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); 任意對象(屬于同一個類)的實例方法引用
如下示例,這里引用的是字符串數組中任意一個對象的compareToIgnoreCase方法。
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase); 構造方法引用
如下示例,這里使用了關鍵字new,創建了一個包含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;
} 什么場景適合使用方法引用
當一個Lambda表達式調用了一個已存在的方法
什么場景不適合使用方法引用
當我們需要往引用的方法傳其它參數的時候,不適合,如下示例:
IsReferable demo = () -> ReferenceDemo.commonMethod("Argument in method."); ?
?
Lambda 和 方法引用實戰
第一步,傳遞代碼
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表達式
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
Java編譯器可以根據Lambda出現的上下文來推斷Lambda表達式參數的類型,所以可以改寫為:
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
Comparator具有一個叫做comparing的靜態方法,可以接受一個Function來提取Comparable鍵值,并生成一個Comparator對象,如下:
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
所以再次改寫:
inventory.sort(comparing((a) -> a.getWeight()));
第四步,使用方法引用
inventory.sort(comparing(Apple::getWeight));
?
復合Lambda表達式
1,比較器復合
a,逆序
inventory.sort(comparing(Apple::getWeight).reversed())
b, 比較器鏈
inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));
2, 謂詞復合
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, 函數復合
a,andThen 相當于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 相當于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
?
轉載于:https://www.cnblogs.com/IvySue/p/6737133.html
總結
以上是生活随笔為你收集整理的《Java 8 实战》(二)—— Lambda的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喂你们八卦的版主能不能不要老是搞一些转眼
- 下一篇: 机房收费系统(VB.NET)——超具体的