《JAVA8開發指南》第二章采用Lambda表達式(二)
注意事項
你會經常看到在接口類上標有@FunctionalInterface的注解。它和標簽@Override很相似,表示方法是可以被覆蓋的。本文中,@FunctionalInterface標簽用來寫在文檔中,表示接口是一個函數接口。編譯器也將在接口注解不符合函數接口定義的時候拋錯。
你也將發現一些新的函數接口,比如在包java.util.function裏麵的Function<T, R>和Supplier<T>,你可以通過這些來使用任意形式的lambda表達式。
方法參數
方法參數允許你重用已經存在的方法定義並將它們像lamda表達式一樣傳遞進來。它們的實用性在你寫代碼的時候展現,會使代碼跟單純的使用lambda表達式比更自然更易讀。比如使用lambda表達式找隱藏的文件。
File[] hiddenFiles = mainDirectory.listFiles(f -> f.isHid
den());
使用方法參數,你能夠使用雙冒號直接應用方法isHidden。
File[] hiddenFiles = mainDirectory.listFiles(File::isHidden);
最直接理解lambda表達式的方法參數的方式是叫它"指定方法"。
方法參數的類型有以下四種:
1、靜態方法的方法參數
Function<String, Integer> converter = Integer::parseInt;
Integer number = converter.apply("10");
2、實例方法的方法參數。
意思是將一個對象的方法運用到lambda表達式的第一個參數位置上。
Function<Invoice, Integer> invoiceToId = Invoice::getId;
3、已有對象的實例化方法參數。
Consumer<Object> print = System.out::println;
需要指出的是,這種方法參數在你遇到一個私有輔助方法並想將其注入到另一個方法中時非常有用。
File[] hidden = mainDirectory.listFiles(this::isXML);
private boolean isXML(File f) {
return f.getName.endsWith(".xml");
}
4、構造器參數:
Supplier<List<String>> listOfString = List::new;
大匯合
在本章的開始,你看到了一段冗長的分類變量invoices的java代碼。
Collections.sort(invoices, new Comparator<Invoice>() {
public int compare(Invoice inv1, Invoice inv2) {
return Double.compare(inv2.getAmount(), inv1.getAmount());
}
});
現在,你將看到如何通過我們目前所掌握的java8特性重構這段代碼,使其變得更可讀更簡潔。
首先,Comparator是一個函數接口,它僅定義了一個抽象方法叫“compare”,這個類傳入了兩個相同類型的對象,並返回一個整數。這種情況lambda表達式能夠有很好的表現,如:
Collections.sort(invoices,
(Invoice inv1, Invoice inv2) -> {
return Double.compare(inv2.getAmount(),
inv1.getAmount());
});
我們發現lambda表達式的體僅僅的返回一個簡單的表達式,所以可以使用更加簡潔的形式:
Collections.sort(invoices,
(Invoice inv1, Invoice inv2)
-> Double.compare(inv2.getAmount(),
inv1.getAmount()));
java8中List接口支持sort方法,所以可以使用List代替Collections.sort,如下:
invoices.sort((Invoice inv1, Invoice inv2)
-> Double.compare(inv2.getAmount(),
inv1.getAmount()));
接下來,java8引入了一個靜態輔助方法Comparator.comparing,它使用一個lambda參數抽取出比較的key值。該地方最後生成了一個Compare對象。你可以采用如下方法使用它。
Comparator<Invoice> byAmount
= Comparator.comparing((Invoice inv) -> inv.getAmount());
invoices.sort(byAmount);
你可能注意到了,更簡潔的辦法是采用Invoice::getAmount代替(Invoice inv)
-> inv.getAmount()。如下:
Comparator<Invoice> byAmount
= Comparator.comparing(Invoice::getAmount);
invoices.sort(byAmount);
由於getAmount方法返回的是一個double類型的值,所以使用Comparator.comparingDouble指定類型能夠避免一些不必要的問題:
Comparator<Invoice> byAmount
= Comparator.comparingDouble(Invoice::getAmount);
invoices.sort(byAmount);
最後,讓我們整理下代碼,使用import static引入方法,去掉持有Comparator對象的變量,給出本章開頭引入的問題的解決方案。
import static java.util.Comparator.comparingDouble;
invoices.sort(comparingDouble(Invoice::getAmount));
使用Lambda表達式測試
你可能關心lambda表達式如何影響測試。畢竟,lambda表達式引入的行為需要被測試驗證。當你決定測試包含lambda表達式的時候,可以考慮以下兩個方麵。
如果lambda表達式很小,那麼可以去測試使用了lambda表達式的周圍代碼行為。
如果lambda表達式非常複雜,則抽取它們到一個單獨的方法參數中,這樣你就可以將其注入並獨立測試它們了。
總結
本章的重點概念如下:
lambda表達式可以被理解為一種匿名函數。
Lambda表達式和行為參數模型會代碼更靈活更簡潔。
函數接口是一個僅聲明了一個抽象方法的接口。
Lambda表達式隻能用在函數接口上下文中。
在你需要重用一個已有方法時,方法參數比單純的lambda表達式更加自然,隻需要將該方法傳入參數位置即可。
測試時,將複雜的lambda表達式拆開,方便你將它們注入到方法參數中。
最後更新:2017-05-19 14:08:15