閱讀730 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《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();


轉載自 並發編程網 - ifeve.com

最後更新:2017-05-19 14:08:10

  上一篇:go  可口可樂從此徹底廢除“首席營銷官”,設立“首席增長官”
  下一篇:go  他去數了紐約街道上的口香糖漬,竟發現一道貧富鴻溝