首先,思考一個問題,比如說一個養豬廠的廠長要你幫忙設計一個程序,統計他豬場內白豬的 數量。你輕蔑的一笑,這很簡單啊,於是你迫不及待的開工了。
先模擬一些豬的數據:
var pigList = new ArrayList<Pig>();
pigList.add(new Pig("黑色", 120));
pigList.add(new Pig("白色", 300));
pigList.add(new Pig("黑色", 208));
pigList.add(new Pig("白色", 188));
篩選白色的豬:
public class PigFilter {
public static List<Pig> filterWritePig(List<Pig> pigList){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if("白色".equals(pig.getColor())) {
newPigList.add(pig);
}
}
return newPigList;
}
}
這樣就大功告成了,簡直小菜一碟,但是,你纔剛得意沒兩天,那個該死的廠長又找到你了,說他現在還想統計一下黑豬的數量,你微微一笑,這也很簡單,於是,你將上面的代碼複製修改了一番再次大功告成!
public static List<Pig> filterBlackPig(List<Pig> pigList){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if("黑色".equals(pig.getColor())) {
newPigList.add(pig);
}
}
return newPigList;
}
或許這次之後,你就會想到,一旦這廝以後又要統計什麼紅豬黃豬腫麼辦?總不能一直粘貼複製修改吧? 於是你想到了這個辦法,將顏色作爲參數:
public static List<Pig> filterPigByColor(List<Pig> pigList,String color){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if(color.equals(pig.getColor())) {
newPigList.add(pig);
}
}
return newPigList;
}
事畢之後,你長出了一口氣,默默的點燃了一支菸,不禁被自己的聰明才智打動了 。但是沒過幾天,那個該死的廠長又來找到你,說他又要統計重量爲200斤以上的豬,於是你顫抖着雙手又複製粘貼修改了上面的代碼:
public static List<Pig> filterPigByWeight(List<Pig> pigList,int weight){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if(weight<pig.getWeight()) {
newPigList.add(pig);
}
}
return newPigList;
}
然後,你痛定思痛,你會想到能不能再增加一個參數,用來篩選條件呢?於是。。。。
public static List<Pig> filterPig(List<Pig> pigList,int weight,String color,Boolean flag){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if((flag&&weight<pig.getWeight()) || (!flag&&color.equals(pig.getColor()))) {
newPigList.add(pig);
}
}
return newPigList;
}
使用:List<Pig> pigs = PigFilter.filterPig(pigList,0,"黑色",true);
你把改進的代碼交給了廠長,但是廠長抱怨你這個true啊,flase啊是什麼意思?並提出了新的需求,他要統計白豬裏200斤以上的,黑豬裏不足200斤的,豬的不同品種,是公的還是母的。。。。。。。你瞬間奔潰惹。你要添加更多的參數更多的複雜的判斷條件來完成需求 。
於是,你經過了苦苦的思索,終於,你想到了一種方式。你建了一個接口,該接口是豬的篩選條件建模,他的實現是這樣的,比如一頭豬是黑色的嗎?是返回true不是返回false。這種返回一個boolen值得函數,我們有一個專業的裝B的叫法----謂詞。當然不是一定要使用這所謂的“謂詞”,接口你可以隨心所欲的使用任何返回值類型,謂詞只是這種返回布爾值的特殊叫法而已。
public interface PigPredicate {
public Boolean test(Pig pig);
}
它的一個實現類,黑豬篩選:
public class PigBlackPredicate implements PigPredicate{
@Override
public Boolean test(Pig pig) {
// TODO Auto-generated method stub
return "黑色".equals(pig.getColor());
}
}
然後呢,我們就可以利用這個條件進行黑豬篩選:
public static List<Pig> filterPig(List<Pig> pigList,PigPredicate p){
var newPigList = new ArrayList<Pig>();//用於存放符合條件的豬
for(Pig pig : pigList) {
if(p.test(pig)) {
newPigList.add(pig);
}
}
return newPigList;
}
我們就可以這麼食用了:
List<Pig> pigs = PigFilter.filterPig(pigList,new PigBlackPredicate());
這樣,你發現你的代碼靈活多了,針對不同的篩選條件,你只需要添加不同的實現類就可以了。像這種你的篩選方法filterPig的行爲取決於你PigPredicate傳遞的代碼的行爲,就叫做行爲參數化!
但是目前的這種實現方式雖好,卻有兩個問題,第一是你要用到的重要代碼test方法只能包裹在一個對象中(即PigPredicate接口的實現類)進行傳遞 ,第二是你要定義大量的接口實現類,而這些實現類可能你只使用一次而已,爲只使用一次的類而如此大費周章,顯然是有些不值得的。
針對第二個問題,有的童鞋會說,你可以使用匿名內部類啊!沒錯,下面我們就可以使用匿名內部類,來實現行爲參數化。
List<Pig> pigs = PigFilter.filterPig(pigList,new PigPredicate() {
@Override
public Boolean test(Pig pig) {
// TODO Auto-generated method stub
return 211 == pig.getWeight();
}
});
這樣,我們也可以成功篩選體重爲211斤的豬。但是這種匿名內部類還是很羅嗦,看着也很不爽。
那麼爲了改進這個問題,java8引進了lambda表達式,那麼上面的代碼就可以改進爲:
List<Pig> pigs2 = PigFilter.filterPig(pigList,(Pig pig)->211==pig.getWeight());
怎麼樣?是不是很炫酷?(參數—>返回結果)=傳遞的行爲代碼(本例種的test,也就是你定義的篩選條件接口的實現!)。
lambda表達式同樣也解決了上述的第一個問題,就是我們可以將我們的方法直接作爲參數傳遞,而無需用對象進行包裹,使我們的方法成爲了一等值!
看到這裏,可能有的童鞋會有疑問,如果接口中有多個方法怎麼辦,能使用lambda表達式嗎?答案是否定的,並不是所有接口都可以使用Lambda表達式,只有函數式接口可以。按照Java8函數式接口的定義,其只能有一個抽象方法,否則就不是函數時接口,就無法用Lambda表達式。可以使用@FunctionalInterface標註函數式接口,在編譯時提前發現錯誤。
java8 API中很多方法都可以用不同的行爲來參數化。比如list.sort排序,線程的創建(Thread t = new Thread(() -> System.out.println("Hello world"));),GUI事件處理等等。
本章小姐姐:
1.行爲參數化,就是一個方法接受多個不同的行爲作爲參數,並在內部使用它們,完成不同行爲的能力。
2.行爲參數化可讓代碼更好地適應不斷變化的要求,減輕未來的工作量。
3.傳遞代碼,就是將新行爲作爲參數傳遞給方法。但在Java 8之前這實現起來很囉嗦。爲接口聲明許多隻用一次的實體類而造成的囉嗦代碼,在Java 8之前可以用匿名類來減少。
4.Java API包含很多可以用不同行爲進行參數化的方法,包括排序、線程和GUI處理。