> 假設一個情景: 找出滿足條件的Hero
> 本教程將從使用普通方法,匿名類,以及Lambda這幾種方式,逐漸的引入Lambda的概念
## 步驟 1 : 普通方法
TestLambda.java:
```
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TestLambda {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("篩選出 hp>100 && damage<50的英雄");
filter(heros);
}
private static void filter(List<Hero> heros) {
for (Hero hero : heros) {
if (hero.hp > 100 && hero.damage < 50) {
System.out.println(hero);
}
}
}
}
```
## 步驟 2 : 匿名類方式
首先準備一個接口HeroChecker,提供一個test(Hero)方法
```
package lambda;
public interface HeroChecker {
public boolean test(Hero h);
}
```
然后通過匿名類的方式,實現這個接口
```
HeroChecker checker = new HeroChecker() {
public boolean test(Hero h) {
return (h.hp>100 && h.damage<50);
}
};
```
接著調用filter,傳遞這個checker進去進行判斷,這種方式就很像通過Collections.sort在對一個Hero集合排序,需要傳一個Comparator的匿名類對象進去一樣。
```
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TestLambda {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("使用匿名類的方式,篩選出 hp>100 && damage<50的英雄");
HeroChecker checker = new HeroChecker() {
@Override
public boolean test(Hero h) {
return h.hp > 100 && h.damage < 50;
}
};
filter(heros, checker);
}
private static void filter(List<Hero> heros, HeroChecker checker) {
for (Hero hero : heros) {
if (checker.test(hero)) {
System.out.println(hero);
}
}
}
}
```
## 步驟 3 : Lambda方式
使用Lambda方式篩選出數據
`filter(heros,(h)->h.hp>100 && h.damage<50);
`
同樣是調用filter方法,從上一步的傳遞匿名類對象,變成了傳遞一個Lambda表達式進去
`h->h.hp>100 && h.damage<50
`
咋一看Lambda表達式似乎不好理解,其實很簡單,下一步講解如何從一個匿名類一點點演變成Lambda表達式
```
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TestLambda2 {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("使用Lambda的方式,篩選出 hp>100 && damage<50的英雄");
filter(heros, h -> h.hp > 100 && h.damage < 50);
}
private static void filter(List<Hero> heros, HeroChecker checker) {
for (Hero hero : heros) {
if (checker.test(hero)) {
System.out.println(hero);
}
}
}
}
```
## 步驟 4 : 從匿名類演變成Lambda表達式
Lambda表達式可以看成是匿名類一點點演變過來
1. 匿名類的正常寫法
```
HeroChecker c1 = new HeroChecker() {
public boolean test(Hero h) {
return (h.hp>100 && h.damage<50);
}
};
```
2. 把外面的殼子去掉
只保留方法參數和方法體
參數和方法體之間加上符號 ->
```
HeroChecker c2 = (Hero h) ->{
return h.hp>100 && h.damage<50;
};
```
3. 把return和{}去掉
```
HeroChecker c3 = (Hero h) ->h.hp>100 && h.damage<50;
```
4. 把 參數類型和圓括號去掉(只有一個參數的時候,才可以去掉圓括號)
```
HeroChecker c4 = h ->h.hp>100 && h.damage<50;
```
5. 把c4作為參數傳遞進去
```
filter(heros,c4);
```
6. 直接把表達式傳遞進去
```
filter(heros, h -> h.hp > 100 && h.damage < 50);
```
```
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TestLambda1 {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("使用匿名類的方式,篩選出 hp>100 && damage<50的英雄");
// 匿名類的正常寫法
HeroChecker c1 = new HeroChecker() {
@Override
public boolean test(Hero h) {
return (h.hp > 100 && h.damage < 50);
}
};
// 把new HeroChcekcer,方法名,方法返回類型信息去掉
// 只保留方法參數和方法體
// 參數和方法體之間加上符號 ->
HeroChecker c2 = (Hero h) -> {
return h.hp > 100 && h.damage < 50;
};
// 把return和{}去掉
HeroChecker c3 = (Hero h) -> h.hp > 100 && h.damage < 50;
// 把 參數類型和圓括號去掉
HeroChecker c4 = h -> h.hp > 100 && h.damage < 50;
// 把c4作為參數傳遞進去
filter(heros, c4);
// 直接把表達式傳遞進去
filter(heros, h -> h.hp > 100 && h.damage < 50);
}
private static void filter(List<Hero> heros, HeroChecker checker) {
for (Hero hero : heros) {
if (checker.test(hero)) {
System.out.println(hero);
}
}
}
}
```
## 步驟 6 : 匿名方法
與匿名類 概念相比較,
Lambda 其實就是匿名方法,這是一種把方法作為參數進行傳遞的編程思想。
雖然代碼是這么寫
```
filter(heros, h -> h.hp > 100 && h.damage < 50);
```
但是,Java會在背后,悄悄的,把這些都還原成匿名類方式。
引入Lambda表達式,會使得代碼更加緊湊,而不是各種接口和匿名類到處飛。
## 步驟 7 : Lambda的弊端
Lambda表達式雖然帶來了代碼的簡潔,但是也有其局限性。
1. 可讀性差,與啰嗦的但是清晰的匿名類代碼結構比較起來,Lambda表達式一旦變得比較長,就難以理解
2. 不便于調試,很難在Lambda表達式中增加調試信息,比如日志
3. 版本支持,Lambda表達式在JDK8版本中才開始支持,如果系統使用的是以前的版本,考慮系統的穩定性等原因,而不愿意升級,那么就無法使用。
Lambda比較適合用在簡短的業務代碼中,并不適合用在復雜的系統中,會加大維護成本。
## 練習
把比較器-Comparator,按照從匿名類演變成Lambda表達式的步驟,改寫為Lambda表達式
```
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
public class TestLambda3 {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 10; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
System.out.println("初始化后的集合:");
System.out.println(heros);
System.out.println("使用匿名類的方式,篩選出 hp>100 && damage<50的英雄");
// Collections.sort(heros);
Comparator<Hero> c = new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
if (o1.hp > o2.hp) {
return 1;
} else {
return -1;
}
}
};
// Collections.sort(heros, c);
Collections.sort(heros, (h1, h2) -> (h1.hp >= h2.hp) ? 1 : -1);
System.out.println("*****************排序過后********************");
for (Hero hero : heros) {
System.out.println(hero);
}
System.out.println("*****************排序結束********************");
filter(heros, h -> h.hp > 0 && h.damage > 40);
}
private static void filter(List<Hero> heros, HeroChecker checker) {
for (Hero hero : heros) {
if (checker.test(hero)) {
System.out.println(hero);
}
}
}
}
```
> 并不是所有接口都可以使用Lambda表達式,只有函數式接口可以。
按照Java8函數式接口的定義,其只能有一個抽象方法,否則就不是函數時接口,就無法用Lambda表達式。
可以使用@FunctionalInterface標注函數式接口,在編譯時提前發現錯誤。