目前,当要把新的行为传递给filterApples方法的时候,你不得不声明好几个实现ApplePredicate接口的类,然后实例化
好几个只会提到一次的ApplePredicate对象。下面的程序总结了你目前看到的一切。这真是很啰嗦,很费时间!
行为参数化:用谓词筛选苹果
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());
}
});
但匿名类还是不够好。第一,它往往很笨重,因为它占用了很多空间
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple a){
return "red".equals(a.getColor());
}
});
第二,很多程序员觉得它用起来很让人费解。
public class MeaningOfThis{
public final int value = 4;
public void doIt(){
int value = 6;
Runnable r = new Runnable(){
public final int value = 5;
public void run(){
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
public static void main(String...args){
MeaningOfThis m = new MeaningOfThis();
m.doIt();
}
}
//答案是5,因为this指的是包含它的Runnable,而不是外面的类MeaningOfThis。
第六次尝试:使用Lambda 表达式
List<Apple> result = filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
第七次尝试:将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;
}
List<Apple> redApples =filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
List<Integer> evenNumbers =filter(numbers, (Integer i) -> i % 2 == 0);
真实的事例:
1)用Comparator 来排序
对集合进行排序是一个常见的编程任务。比如,你的那位农民朋友想要根据苹果的重量对库存进行排序,或者他可能改了主意,希望你根据颜色对苹果进行排序。听起来有点儿耳熟?是的,你需要一种方法来表示和使用不同的排序行为,来轻松地适应变化的需求。在Java 8中,List自带了一个sort方法(你也可以使用Collections.sort)。sort的行为可以用java.util.Comparator对象来参数化,它的接口如下:
// java.util.Comparator
public interface Comparator<T> {
public int compare(T o1, T o2);
}
因此,你可以随时创建Comparator的实现,用sort方法表现出不同的行为。比如,你可以使用匿名类,按照重量升序对库存排序:
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
如果农民改了主意,你可以随时创建一个Comparator来满足他的新要求,并把它传递给sort方法。而如何进行排序这一内部细节都被抽象掉了。用Lambda表达式的话,看起来就是这样:
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
2)用Runnable 执行代码块
线程就像是轻量级的进程:它们自己执行一个代码块。但是,怎么才能告诉线程要执行哪块代码呢?多个线程可能会运行不同的代码。我们需要一种方式来代表稍候执行的一段代码。在Java里,你可以使用Runnable接口表示一个要执行的代码块。
// java.lang.Runnable
public interface Runnable{
public void run();
}
Thread t = new Thread(() -> System.out.println("Hello world"));
小结
1. 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
2. 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。
3. 传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦。为接
4. 口声明许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类来减少。
5. Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程等。