一.什么是行為參數化呢 說白了就是將一段行為當作參數傳入一個方法中唄,那么這段行為是什么呢?
答:那也是一個方法咯。
Java在1.8版本引入了行為參數化的概念,首先,我們先看一小段代碼
public class test { public static void main(String[] args){ Consumer consumer = new Consumer() { @Override public void accept(String s) { System.out.println(s); } };
show(consumer ,"hello world");
}public static void show(Consumer<String> consumer,String text){consumer.accept(text);
}
復制代碼
} 輸出結果當然是:hello world 啦。
可是你想這有什么呢?不就是寫了一個Consumer接口,然后寫一個匿名類重寫他的accept方法,然后帶入Consumer到show方法調用它咩?Java沒更新前也可以這樣啊,只不過是傳參了一個對象,然后在方法內調用了對象的方法。
那么我們再來看看下面這段代碼,看看它和上面的代碼有什么不同:
public class test { public static void main(String[] args){ /Consumer consumer = new Consumer() { @Override public void accept(String s) { System.out.println(s); } };/
show(System.out::println ,"hello world");
}public static void show(Consumer<String> consumer,String text){consumer.accept(text);
}
復制代碼
} 注意!代碼中3到8行已經注釋了哦。
現在看看:show方法接受一個Consumer對象和字符串,然后調用對象的accept方法,這里都沒變;可是著方法中調用show方法的時候傳遞的第一個參數不再是Consumer對象了,而是System.out::println,而這是什么意思呢?
System.out::println 是方法引用,方法引用的格式就是:類名::方法名 或者 對象名::成員方法名
我們都知道,在方法重寫和重載中我們是怎么做的呢?在子類中寫一個和主類中被重寫方法方法名相同、參數類型、順序相同的方法,那jdk是根據什么來判斷是否是重寫或者重載呢?沒錯,就是靠函數簽名來判斷的,就比如上面代碼中的show方法,它的函數簽名就是:名為show、接受兩個參數并且第一個參數是Consumer類型、第二個參數是String類型的函數,如果返回類型相同,則是重寫;否則是重載。Java根據你的方法引用來確定你的方法簽名和返回類型,如果和形參中Consumer對象中的方法相同的話,就可以使用。
那么我們回到代碼中去,我們在主方法中調用show方法時傳入的第一個參數就是一個:無返回值,接受一個泛型參數的方法簽名的方法引用。
你也可以傳入其他方法進去,只要方法簽一致就可以啦。就像下面一樣:
public class test { public static void main(String[] args){ /Consumer consumer = new Consumer() { @Override public void accept(String s) { System.out.println(s); } };/ test one = new test();
show(one::otherMethod ,"hello world");
}public void otherMethod(T t){int i = 0;System.out.println(i + t.toString());
}public static void show(Consumer<String> consumer,String text){consumer.accept(text);
}
復制代碼
} 結果是輸出:0hello world
你也可以將show方法的第二個參數換成一個集合,然后在第一個參數中傳入其他方法引用來對集合進行操作哦。
在上面我們說到在帶哦用show方法時傳入的方法引用的方法簽名、返回類型要和Consumer對象中方法的一致,那么這個Consumer到底是什么呢?它的內部只有一個方法嗎?如果有多個方法的話怎樣根據傳入的方法引用和他的眾多方法比較呢?
答:Consumer其實是一個接口,一個函數式接口,話不多說,直接上源碼
package java.util.function;
import java.util.Objects;
@FunctionalInterface public interface Consumer {
void accept(T t);default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };
}
復制代碼
} 大家看,是不是感覺有些不對呢?Java接口明明不能所有實現方法的,現在卻多了一個default方法被實現,嘿嘿,這是Java8的新特性,這個我會在之后的帖子中繼續和大家分享。大家要注意的是@FunctionalInterface這個注解,它表明這個接口是一個函數式接口,函數式接口的定義就是:一個有且只有一個抽象方法,但是可以有多個非抽象方法的接口。非抽象方法就是default方法,可以有多個,但是抽象方法只能有一個,那么上面關于傳入方法引用怎樣匹配方法簽名和返回值的疑問你們理解了嗎?
就是說在上面的例子中我在show方法定義的Consumer中只有一個抽象方法accept,他是一個無返回值,接受一個泛型參數的方法,而我在調用show方法的時候也傳入了和accept方法返回值一致,參數相同的方法引用,這就是方法重寫,在上面的例子中我傳入System.out::println時就是無形之中創建了一個匿名類,然后重寫了他的accept方法,重寫的方法就是System.out.println()方法。神奇咩?嘿嘿...
等等,還沒完...Consumer接口提供一個無返回值類型接受一個參數的抽象方法供大家使用,那么如果我有其他需求呢?假如我需要一個有返回值的抽象方法供我使用呢?
別急,Java提供了一系列函數式接口來供我們按照需求使用,如果沒有自己需要的接口的話我們也可以自己寫一個函數式接口來使用。
函數式接口 函數描述符 Predicate T->boolean Consumer T->void Function<T,R> T->R Supplier ()->T UnaryOperator T->T BinaryOperator (T,T)->T
BiPredicate<L,R> (L,R)->boolean BiConsumer<T,U> (T,U)->void BiFunction<T,U,R> (T,U)->R 函數描述符是什么呢?這就要說到Lambda表達式了,在下一篇博客中我會講解。(本人是小白大學生一枚,如有不對或者不當之處,還請各位前輩指點,嘿嘿)