草捏对Lambda表达式的了解停留在用IDEA自动替换匿名类的程度,以为Lambda表达式只是把代码缩短了而已,不过就是一个语法糖。所以一直不屑一顾,没系统学习。“不过就是代码短一点嘛,没啥大不了”。但通过学习才知道Lambda表达式不仅仅是把代码换了种表达方式,或许更重要的是背后的思想——行为参数化。
所谓的行为参数化,指的是我们可以通过参数传递的形式去指定代码的行为。是不是很眼熟,学过设计模式的童鞋,基本都是从策略模式开始学起的。策略模式是指面向接口编程,通过使用不同的实现类,改变具体的行为。行为参数化和策略模式的效果类似,只是多了个参数化,通过传递参数来指定行为。
下面草捏给大家讲个关于挑苹果的小故事。
梅梅开始计划每天吃一个苹果,于是吩咐草捏去超市采购。草捏(一个没有生活经验的男人)买苹果的原则:不是烂的就行。
// 不是烂的就行
// 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;
}
兴致勃勃的买完回到家。
:“草捏,这苹果里咋还有个绿的,我喜欢吃红苹果,要买红的!!!”
:“啊,知道了,知道了, 买红的,下次买红的。”
// 这次知道了不仅不是烂的,还要买红的
// 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;
}
第二次买苹果归来,等待检验。
:“嗯,这次是清一色的红苹果了,可是我喜欢吃小点的苹果,更可爱些,直径应该小于5厘米。”
:“直径...小于...5厘米...”
:“有问题吗?”
:“好的,好的,下次买小于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);
真是妙啊~ 实乃居家生活之必备~