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