通過行爲參數化傳遞代碼(下)

目前,當要把新的行爲傳遞給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包含很多可以用不同行爲進行參數化的方法,包括排序、線程等。

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