通过行为参数化传递代码(下)

目前,当要把新的行为传递给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包含很多可以用不同行为进行参数化的方法,包括排序、线程等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章