Lambda表达式
商家采购苹果,对每一个苹果都进行了编号,并记录其颜色和重量,用下面的结构表示
Apple {id, color, weight}
需求1:从所有的苹果中筛选出绿颜色的苹果
so easy,代码张手就来。
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( "green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
需求2:从所有苹果中筛选出红颜色的苹果
再写一个filterRedApples方法呗,代码与filterGreenApples几乎一样,只是green改成red,这种做法明显太不优雅,要是再加一种颜色,难道要再加一个几乎一样的方法吗?
于是,我们对上面的方法改装一下,将颜色提取成参数,这样想找什么颜色的苹果都可以,由调用者自己决定,问题就轻松解决。
public static List<Apple> filterApples(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( color.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
需求3:从所有苹果中筛选重量小于100g的绿色的苹果
**,刚把颜色的问题解决好,居然使用其它属性,更关键的问题是,未来是不是还会有更多的属性加入,思之极恐。
看来,我们必须把这个方法写得更加通用一点。
分析一下代码:
这个方法的作用是筛选,每次的循环操作都是不变的,变化的部分就是判断的条件。
那我们把不变的部分固定下来,把变化的部分抽离出来,把这个条件判断用一个固定的方法来代替。
Let’s go!
使用策略模式进行改造
创建一个筛选器接口,入参为Apple对象(这样就包含了Apple的所有属性),出参为boolean。
interface AppleFilter {
boolean test(Apple apple);
}
public static List<Apple> filterApples(List<Apple> inventory, AppleFilter filter) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( filter.test(apple) ) {
result.add(apple);
}
}
return result;
}
需要绿苹果,就创建一个绿苹果筛选器
static class GreenAppleFilter implements AppleFilter {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
需要红苹果,就创建一个红苹果筛选器
static class GreenAppleFilter implements AppleFilter {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
想要什么样的条件,就自行创建一个相应的筛选器即可,需求完美解决。这其实就是一个策略模式的诞生。
但是这样的代码有没有什么缺点?
没错,就是会多出来一堆的筛选器类,让整个项目变得臃肿。
使用匿名内部类进行改造
JDK不是有匿名内部类嘛,这样就可以避免上面的问题了。改造不能停。
List<Apple> greenApples = filterApples(list, new AppleFilter() {
@Override
public boolean test(Apple apple) {
return "green".equals(apple);
}
});
List<Apple> redApples = filterApples(list, new AppleFilter() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple);
}
});
一切似乎烟消云散,至于内部类所存在的缺点,似乎也不是我们力所能及能够改变得了的,故事该大结局了。
的确,在JDK1.8之前,这就是终极状态了,但现在一切又有了新的转机。
将简化进行到底
匿名内部类中的代码还有什么是可以简化的?
这里的匿名内部类所实现的接口(AppleFilter),只有一个方法(test),所以在这里创建这个内部类的意义就是为了调用它的实现方法。
我们关注的不是 AppleFilter 接口的定义,也不是 test 方法的定义,因为这些都已经定义好了,我们关注的只是 test 方法的实现。
使用 Lambda 表达式
List<Apple> greenApples = filterApples(list, apple -> "green".equals(apple));
List<Apple> redApples = filterApples(list, apple -> "red".equals(apple));
第一次看这样的代码也许并不能马上理解,这并不要紧,后面的章节会详细解释,但请记住这个写法。
通用实现
现在,我们实现了苹果的筛选,那么香蕉,菠萝呢?是不是可以实现一个更通用的方法?答案当然是肯定的。
使用泛型进行方法改造
interface Filter<T> {
boolean test(T apple);
}
public static <T> List<T> filter(List<T> inventory, Filter<T> filter) {
List<T> result = new ArrayList<>();
for (T item: inventory) {
if ( filter.test(item) ) {
result.add(item);
}
}
return result;
}