1、在Java8之前如果需要从一个苹果对象集合中选出绿色的苹果,通常是这么做的。
public static List<Apple> filterGreenApples(List<Apple> list){
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if( "green".equals(apple.getColor())){
result.add(apple);
}
}
return result;
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if( "green".equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
2、 但是有经验的程序员不会像上面那么写,为了让程序更具有灵活性,会选择将颜色变成一个参数。
public static List<Apple> filterApplesByColor(List<Apple> list, String color){
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(color.equals(apple.getColor())){
result.add(apple);
}
}
return result;
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(color.equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
这么做的好处就是这个方法可以应对需求的变更,可以用来过滤不同颜色的苹果,不仅仅是绿色的苹果。
除了根据颜色过滤之外,还可能需要根据其他属性过滤,比如重量,这时候你又会写一个方法来处理这个需求。
public static List<Apple> filterApplesByWeight(List<Apple> list,
int weight){
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
3、 这样做没什么问题,但是你复制了大部分代码,这违反了Don't Repeat Yourself的原则,所以我们需要更加优雅的解决办法。
我们先定义一个测试接口
public interface ApplePredicate {
boolean test(Apple apple);
}
boolean test(Apple apple);
}
然后提供这个测试接口的实现类型
public class AppleHeavyWeightPredicate
implements ApplePredicate{
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 200;
}
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 200;
}
}
public class AppleGreenColorPredicate
implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
然后将这些测试接口的实现类型的实例作为一种策略传递给刷选苹果的方法, 这就是策略模式。
public static List<Apple> filterApples(List<Apple> list, ApplePredicate predicate){
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(predicate.test(apple)){
result.add(apple);
}
}
return result;
List<Apple> result = new ArrayList<>();
for(Apple apple: list){
if(predicate.test(apple)){
result.add(apple);
}
}
return result;
}
这样就实现了根据任一条件进行过滤的方法。
4、策略模式已经非常灵活可扩展,但是仍然有缺点, 就是冗余代码很多,需要
为每个策略预先定义一个实现类型 。解决这个问题可以使用匿名内部类。
List<Apple> redApples =
filterApples(appleList,
new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
5、匿名内部类 依然有很多冗余代码,
一个测试接口的实现类型实际上只有一行有效代码,所以需要不断优化。这个时候就轮到传说中的Lambda表达式
上场了。
List<Apple> redApples =
filterApples(appleList, (apple) ->
"red".equals(apple.getColor()));
一行代码解决问题, 这就是
Lambda表达式的威力。
6、对过滤方法进一步抽象, 使这个方法可以适用任何类型的集合的过滤。
public static <
T> List<
T> filterApples(List<
T> list, ApplePredicate<
T> p){
List< T> result = new ArrayList<>();
for( T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
List< T> result = new ArrayList<>();
for( T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}