上一篇:java1.8实战学习(四)——通过行为参数化传递代码
下一篇:
上节答案:
首先,你需要一种表示接受Apple并返回一个格式String值的方法。前面我们在编写ApplePredicate接口的时候,写过类似的东西:
public interface AppleFormatter{
String accept(Apple a);
}
现在你就可以通过实现AppleFormatter方法,来表示多种格式行为了:
public class AppleFancyFormatter implements AppleFormatter{
public String accept(Apple apple){
String characteristic = apple.getWeight() > 150 ? "heavy" :
"light";
return "A " + characteristic +
" " + apple.getColor() +" apple";
}
}
public class AppleSimpleFormatter implements AppleFormatter{
public String accept(Apple apple){
return "An apple of " + apple.getWeight() + "g";
}
}
最后,你需要告诉prettyPrintApple方法接受AppleFormatter对象,并在内部使用它们。你可以给prettyPrintApple加上一个参数:
public static void prettyPrintApple(List<Apple> inventory,
AppleFormatter formatter){
for(Apple apple: inventory){
String output = formatter.accept(apple);
System.out.println(output);
}
}
现在你就可以给prettyPrintApple方法传递多种行为了。为此,你首先要实例化AppleFormatter的实现,然后把它们作为参数传给prettyPrintApple:
prettyPrintApple(inventory, new AppleFancyFormatter());
这将产生一个类似于下面的输出:
A light green apple
A heavy red apple
…
或者试试这个:
prettyPrintApple(inventory, new AppleSimpleFormatter());
这将产生一个类似于下面的输出:
An apple of 80g
An apple of 155g
你已经看到,可以把行为抽象出来,让你的代码适应需求的变化,但这个过程很啰嗦,因为你需要声明很多只要实例化一次的类。让我们来看看可以怎样改进。
对付啰嗦
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
public class AppleGreenColorPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "green".equals(apple.getColor());
}
}
public class FilteringApples{
public static void main(String...args){
List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
new Apple(155, "green"),
new Apple(120, "red"));
List<Apple> heavyApples =
filterApples(inventory, new AppleHeavyWeightPredicate());
List<Apple> greenApples =
filterApples(inventory, new AppleGreenColorPredicate());
}
public static List<Apple> filterApples(List<Apple> inventory,
ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory){
if (p.test(apple)){
result.add(apple);
}
}
return result;
}
}
费这么大劲儿真没必要,能不能做得更好呢?Java有一个机制称为匿名类,它可以让你同时声明和实例化一个类。
第五次尝试:使用匿名类
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple){
return "red".equals(apple.getColor());
}
});
但匿名类还是不够好。第一,它往往很笨重,因为它占用了很多空间。第二,很多程序员觉得它用起来很让人费解。
第六次尝试:使用 Lambda 表达式
上面的代码在Java 8里可以用Lambda表达式重写为下面的样子:
List<Apple> result =
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
它看起来更像问题陈述本身了。我们现在已经解决了啰嗦的问题。
第七次尝试:将 List 类型抽象化
在通往抽象的路上,我们还可以更进一步。目前,filterApples方法还只适用于Apple。你还可以将List类型抽象化,从而超越你眼前要处理的问题:
public interface Predicate<T>{
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
现在你可以把filter方法用在香蕉、桔子、Integer或是String的列表上了。这里有一个使用Lambda表达式的例子:
List<Apple> redApples =
filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
List<Integer> evenNumbers =
filter(numbers, (Integer i) -> i % 2 == 0);
你现在在灵活性和简洁性之间找到了最佳平衡点,这在Java 8之前是不可能做到的!