《JAVA8開發指南》第二章采用Lambda表達式(一)
采用Lambda表達式
本章,你將學習到如何采用JAVA8的重要特性Lambda表達式。首先,你要了解“行為參數”這種模式。該模式能夠使你寫出來的代碼適應需求變化。然後,你將看到該模式如何使得Lambda表達式的使用與以往比變得更加簡潔。然後,你將學習如何精確地定位Lambda表達式的使用場景和使用方式。你也將了解JAVA8的另一個特性-方法參數,它能使你的代碼更簡潔更易讀。帶著所有這些新知識實戰一個重構代碼的例子。最後,你也將學習到如何使用Lambda表達式和方法參數。
為什麼使用Lambda表達式
將Lambda表達式引入JAVA中的動機源於一個叫“行為參數”的模式。這種模式能夠解決需求變化帶來的問題,使代碼變得更加靈活。在JAVA8之前,參數模式十分囉嗦。Lambda表達式通過精簡的方式使用行為模式克服了這個缺點。舉個例子,如果你需要找大於一定金額的發票。你可能寫這樣一個indInvoicesGreaterThanAmount方法:
List findInvoicesGreaterThanAmount(List invoi
ces, double amount) {
List result = new ArrayList<>();
for(Invoice inv: invoices) {
if(inv.getAmount() > amount) {
result.add(inv);
}
}
return result;
}
這個方法確實足夠簡單。可是如果你還需要找小於一定金額的發票呢?或者更糟糕的是,你需要從一個指定商戶中找發票,並且也需要找一定數額呢?這時你需要一個方式來參數化指定條件下的過濾行為。下麵我們將定義一個接口InvoicePredicate來描述條件,使用該接口重構上麵的方法。
接口定義
interface InvoicePredicate {
boolean test(invoice inv);
}
重構方法
List<Invoice> findInvoices(List<Invoice> invoices, InvoicePredi
cate p) {
List<Invoice> result = new ArrayList<>();
for(Invoice inv: invoices) {
if(p.test(inv)) {
result.add(inv);
}
}
return result;
}
使用這段代碼,你能夠通過增加一個Invoice對象解決需求變化帶來的問題。你隻需要創建一個不同的InvoicePredicate 對象,將其傳入方法findInvoices中。換句話說,你已經參數化了findInvoices行為。不好的是,使用這個新方法引入了額外的冗餘,來看下麵的代碼:
List<Invoice> expensiveInvoicesFromOracle
= findInvoices(invoices, new InvoicePredicate() {
public test(Invoice inv) {
return inv.getAmount() > 10_000
&& inv.getCustomer() == Customer.ORACLE;
}
});
換句話說,代碼變得更靈活的同時可讀性變差了。最理想的狀態是,代碼靈活性和可讀性兼備。Lambda表達式的引入能夠做到這一點。通過使用它重構上麵的代碼如下:
List<Invoice> expensiveInvoicesFromOracle
= findInvoices(invoices, inv ->
inv.getAmount() > 10_000
&& inv.getCustomer() ==
Customer.ORACLE);
Lambda表達式的定義
現在知道了為什麼需要在代碼中引入Lambda表達式,也是時候精準的了解下Lambda表達式的定義。簡單的來講,lambda表達式是一個能夠被傳遞的匿名函數,我們仔細看看這個定義:
匿名
Lambda表達式是匿名的,因為它沒有像普通方法那樣有一個明確的名稱。它有點兒像匿名類,因為兩者都沒有明確的名稱。
函數
一個 Lambda表達式像一個方法,它包含一串參數、一個體、一個返回類型和一串可能拋出的錯誤。和方法不同的是,它沒有被聲明為特殊類的一部分。
傳遞
一個 Lambda表達式能夠作為一個方法的參數被傳遞,也能夠作為一個結果被返回。
Lambda表達式語法
在寫lambda表達式之前,需要知道它的語法。在本書中已經出現過lambda表達式:
Runnable r = () -> System.out.println("Hi");
FileFilter isXml = (File f) -> f.getName().endsWith(".xml");
這兩個lambda表達式有三個部分:
- 一串參數,比如 (File f)
- 一個有兩個字符組成的箭頭:- 和 >
- 一個體,比如f.getName().endsWith(".xml")
lambda表達式有兩種形式。當你的lambda表達式體中包含一個語句的時候可以采用第一種:
(parameters) -> expression
當你的lambda表達式體中包含一個或多個語句的時候可以使用第二種形。式請注意,你必須使用大括號將表達式包含進來:
(parameters) -> { statements;}
大體來說,如果lambda表達式參數的類型被間接的指出過,可以去掉類型聲明。另外,如果參數個數隻是一個,那麼圓括號也可以被去掉。
Lambda表達式使用場景
現在,了解了如何寫一個lambda表達式,接下來的問題是考慮使用lambda表達式的使用方式和使用場景。簡單的說,你可以在函數接口中使用lambda表達式。函數接口裏包含一個抽象方法。比如上文的兩個lambda表達式。
Runnable r = () -> System.out.println("Hi");
FileFilter isXml = (File f) ->
f.getName().endsWith(".xml");
Runnable就是一個函數接口,因為它包含了一個抽象方法run。FileFilter也是一個函數接口,因為它也定義了一個抽象方法叫accept。
@FunctionalInterface
public interface Runnable {
void run();
}
@FunctionalInterface
public interface FileFilter {
boolean accept(File pathname);
lambda表達式重要的一點就是讓你創建函數接口的實例。lambda表達式的體提供了函數接口中單個抽象方法的實現。因此,下麵使用匿名類和lamba表達式實現的Runnable的結果是一直的。
Runnable r1 = new Runnable() {
public void run() {
System.out.println("Hi!");
}
};
r1.run();
Runnable r2 = () -> System.out.println("Hi!");
r2.run();
最後更新:2017-05-19 14:08:10