草捏對Lambda表達式的瞭解停留在用IDEA自動替換匿名類的程度,覺得Lambda表達式只是把代碼縮短了而已,不過就是一個語法糖。因此一直不屑一顧,沒系統學習。「不過就是代碼短一點嘛,沒啥大不了」。但經過學習才知道Lambda表達式不單單是把代碼換了種表達方式,可能更重要的是背後的思想——行爲參數化。java
所謂的行爲參數化,指的是咱們能夠經過參數傳遞的形式去指定代碼的行爲。是否是很眼熟,學過設計模式的童鞋,基本都是從策略模式開始學起的。策略模式即是面向接口編程,經過使用不一樣的實現類,改變具體的行爲。行爲參數化和策略模式的效果相似,只是多了個參數化,經過傳遞參數指定了具體的形式。編程
下面草捏給你們講個關於挑蘋果的小故事。設計模式
梅梅開始計劃天天吃一個蘋果,因而吩咐草捏去超市採購。草捏(一個沒有生活經驗的男人)買蘋果的原則就是否是爛的就行。app
// 不是爛的就行 // isRotten = false public static List<Apple> filterApple(List<Apple> apples, boolean isRotten) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (apple.getRotten().equals(isRotten)) { result.add(apple); } } return result; }
興致勃勃的買完回到家。ide
😠:「草捏,這蘋果裏咋還有個綠的🍎🍎🍎🍏,我喜歡吃紅蘋果,要買紅的!!!」函數
😅:「啊,知道了,知道了, 買紅的,下次買紅的。」學習
// 此次知道了不只不是爛的,還要買紅的 // isRotten = false, color = "red" public static List<Apple> filterApple(List<Apple> apples, boolean isRotten, boolean color) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (apple.getRotten().equals(isRotten) && apple.getColor().equals(color)) { result.add(apple); } } return result; }
第二次買蘋果歸來,等待檢驗。spa
🙂:「嗯,此次是清一色的紅蘋果了🍎🍎🍎🍎,但是我喜歡吃小點的蘋果,更可愛些,直徑應該小於5釐米。」設計
😨:「直徑...小於...5釐米...」code
😠:「有問題嗎?」
😅:「好的,好的,下次買小於5釐米的。」
// 又要加一個參數,直徑小於5釐米 // isRotten = false, color = "red", diameter = 5 public static List<Apple> filterApple(List<Apple> apples, boolean isRotten, boolean color, double diameter) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (apple.getRotten().equals(isRotten) && apple.getColor().equals(color) && (apple.getDiameter() < diameter)) { result.add(apple); } } return result; }
草捏發現,函數的參數已經有4個了,已經不少了,是否是能夠考慮改寫一下,傳遞的參數都是挑選蘋果的相關標準,而後在函數中根據這些參數來篩選,是否是能夠把這些參數抽象成一個結構體,這裏抽象成一個Apple類型的變量。
public static List<Apple> filterApple(List<Apple> apples, Apple standard) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (apple.getRotten().equals(standard.isRotten) && apple.getColor().equals(standard.color) && (apple.getDiameter() < standard.diameter)) { result.add(apple); } } return result; }
草捏想這下應該完美了吧,直到第三次回來。
😀:「草捏,我又想吃大蘋果了,下次你買大蘋果回來吧。直徑大於5釐米的那種。」
這需求變的可真快啊。要大蘋果,那就是修改<
爲>
,簡單!
public static List<Apple> filterApple(List<Apple> apples, Apple standard) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (apple.getRotten().equals(standard.isRotten) && apple.getColor().equals(standard.color) && // 篩選大蘋果 (apple.getDiameter() > standard.diameter)) { result.add(apple); } } return result; }
可是看着這兩個版本的代碼,草捏察覺filterAppple
中每次變動的是判斷蘋果是否符合標準的代碼,至於遍歷apples
和根據判斷結果加入到result
中這部分是不變化的。因此若是能把判斷標準的代碼抽象出來,那每次修改的影響就會更小(不用改動filterApple
方法)。
定義一個判斷蘋果標準的接口:
// 蘋果標準判斷 public interface AppleStandardPredicate { // 是否符合標準 boolean isMeetStandard(Apple apple); }
再來個具體實現類,大蘋果標準判斷:
public class BigAppleStandardPredicate implements AppleStandardPredicate { @Override public boolean isMeetStandard(Apple apple) { if (apple.getRotten().equals(false) && apple.getColor().equals("red") && (apple.getDiameter() > 5)) { return true; } return false; } }
把filterApple
的參數改成AppleStandardPredicate
,這樣filterApple
將不會因蘋果判斷邏輯的變化而變化了。
public static List<Apple> filterApple(List<Apple> apples, AppleStandardPredicate predicate) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (predicate.isMeetStandard(apple)) { result.add(apple); } } return result; }
讓咱們爲filterApple
傳入BigAppleStandardPredicate
:
List<Apple> goodApples = filterApple(apples, new BigAppleStandardPredicate());
但這種寫法比較繁瑣的地方在於須要建立一個實現類,若是用匿名內部類可能會更簡潔寫。
List<Apple> goodApples = filterApple(apples, new AppleStandardPredicate() { @Override public boolean isMeetStandard(Apple apple) { if (apple.getRotten().equals(false) && apple.getColor().equals("red") && (apple.getDiameter() > 5)) { return true; } return false; } });
嗯,類是少建立了一個,但好像也不是那麼簡潔,試着用Lambda再稍微簡化下。
List<Apple> goodApples = filterApple(apples, apple -> { if (apple.getRotten().equals(false) && apple.getColor().equals("red") && (apple.getDiameter() > 5)) { return true; } return false; });
😀:「草捏,我不想吃蘋果了,我想吃蛇果!標準和以前蘋果的同樣。」
🤔:「好的。」
這下該怎麼改呢?行爲仍是原來的行爲,可是類型換了。那就用泛型吧。
把AppleStandardPredicate
改成帶泛型的StandardPredicate
:
public interface StandardPredicate<T> { boolean isMeetStandard(T object); }
把filterApple
改成帶泛型的filter
:
public static <T> List<T> filter(List<T> objects, StandardPredicate<T> predicate) { List<T> result = new ArrayList<>(); for (T object : objects) { if (predicate.isMeetStandard(object)) { result.add(object); } } return result; }
最後在調用filter
時,只是修改了下變量名,其餘都沒改,仍然是原來的邏輯。
List<SnakeApple> goodSnakeApples = filter(snakeApples, snakeApple -> { if (snakeApple.getRotten().equals(false) && snakeApple.getColor().equals("red") && (snakeApple.getDiameter() > 5)) { return true; } return false; });
經過類型抽象化,讓StandardPredicate
和Filter
的適用範圍擴大化了,不只能夠用這段代碼挑蘋果和蛇果,你還能拿着這段代碼去買菜!
🥰:」草捏,去買點捲心菜回來~「
😁:「好的」
List<Cabbage> goodCabbages = filter(cabbages, cabbage -> cabbage.getColor().equals("green"));
甚至還能幫老婆挑化妝品!
🥰:」草捏,520快到了,口紅用完了「
😁:「好的」
List<Lipstick> goodLipsticks = filter(lipsticks, lipstick -> lipstick.getPrice() > 500);
真是妙啊~ 實乃居家生活之必備~