函數式接口
jdk1.8 之前的函數式接口
- java.lang.Runnable
new Thread(()->System.out.println("開啓一個線程!")).start();
- java.util.concurrent.Callable
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(()->"創建了一個線程");
- java.security.PrivilegedAction
AccessController.doPrivileged((PrivilegedAction<Object>) ()->null);
- java.util.Comparator
Collections.sort(list, (Comparator<Integer>) (o1, o2) -> o1.compareTo(o2));
- java.io.FileFilter
File file = new File(rootPath);
File[] files = file.listFiles(var1->true);
- java.awt.event.ActionListener
JButton button1 = new JButton("按鈕");
button1.addActionListener(e->System.out.println("你點擊了按鈕!"));
類似的接口還有如下:
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- javax.swing.event.ChangeListener
使用方式類似,不在此贅述。
jdk1.8 新增的函數式接口
jdk1.8 內置的函數式接口放在包 java.util.function
下,默認在jdk安裝路徑下的 src.zip
中。
這些接口,主要分4大類:
- Consumer(類似於消費者需要傳入參數無返回值)
- Supplier(類似於生產者不需要傳入參數,但有返回值)
- Function(有輸入也有返回)
- Predicate(判斷函數,有輸入也有返回,返回trueor false)
他們特點如下:
接口 | 參數類型 | 返回類型 | 方法 | 用途 |
---|---|---|---|---|
Consumer | T | void | void accept(T t) | 對類型T參數操作,無返回結果 |
Supplier | 無 | T | T get() | 創造T類型參數 |
Function | T | R | R apply(T t) | 對類型T參數操作,返回R類型參數 |
Predicate | T | boolean | boolean test(T t) | 斷言型接口,對類型T進行條件篩選操作 |
Consumer 接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer 的作用僅僅是傳入一個參數,並對其進行操作,它在本質上和無返回值的單參方法是一樣的:
public class Test {
private void test(double money, Consumer<Double> con){
con.accept(money);
}
@org.junit.Test
public void test1(){
double money = 10000;
test(money,m->System.out.println("這次消費了" + m + "元!"));
System.out.println(money);
}
}
其結果如下:
由此可見,Consumer 的作用僅僅只是對傳入的參數進行操作,並不是真正的“消費掉了”,因爲函數式編程推崇的是不可變對象,Lambda表達式不允許修改“值”。
但Consumer的真正意義並不是簡單的實現一個無返回值的單參數方法(或者說行爲),而是由它的default方法所帶來的嵌套調用(連鎖調用),這種調用是無限次的。
public class Test {
private void test(double money, Consumer<Double> con){
con.accept(money);
}
@org.junit.Test
public void test1(){
double money = 10000;
Consumer consumer = m->System.out.println("這次消費了"+m+"元");
Consumer consumer1 = s->System.out.println("這次消費了"+s+"元");
test(money,consumer.andThen(consumer1.andThen(consumer)));
System.out.println(money);
}
}
執行結果如下:
這裏將函數式編程的一大理念——對行爲的抽象,展示的淋漓盡致。
Supplier 接口
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier 的真正作用,也僅僅是爲產生對象而生,
@Test
public void test2(){
System.out.println(((Supplier)()->(int)(Math.random()*100)).get());
}
上述的測試用例僅僅是產生了一個100以內的整型隨機數,但這毫無意義,完全可以簡化成:
System.out.println((int)(Math.random()*100));
但這是兩種編程思想的問題,Supplier提供的是一種行爲,將行爲規範化,爲的是如果其他地方需要產生一個對象(比如100以內的整型,這當然可以直接傳入對象,但如果是一個複雜對象,這個對象的創建過程特別複雜),Supplier可以爲其提供一種產生該對象的規範行爲並直接可作爲參數傳入。
Function 接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function ,顧名思義,接受一個參數,經過處理後,再返回一個結果,其中 andThen()
方法接受一個行爲,並將父方法處理過的結果作爲參數再處理,而 compose()
方法正好相反,它是先自己處理然後將結果作爲參數傳給父方法執行,雖然處理的都是數據,但傳入的卻是行爲。
@Test
public void test3(){
Function function = str->str+"suffix";
Function function1 = str->str+"prefix";
System.out.println(function.apply("111"));
System.out.println(function.andThen(function1).apply("111"));
System.out.println(function.compose(function1).apply("111"));
}
結果如下:
Predicate 接口
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
簡單來說,Predicate 也是一種行爲的描述,對參入的對象作某些判斷,並返回一個boolean值,這些判斷邏輯即這個行爲的具體實現過程,則完全由使用者定義,其中還提供一些簡單的組合判斷,與、或、非以及 isEqual
。
@Test
public void test4(){
Predicate predicate = obj -> obj.equals("");
Predicate predicate1 = obj -> Objects.isNull(obj);
System.out.println(predicate.test(""));
System.out.println(predicate.and(predicate1).test(""));
System.out.println(predicate.or(predicate1).test(""));
System.out.println(predicate.negate().test(""));
System.out.println(predicate.and(predicate1).test(""));
}
其中,and、or
的執行順序與java && 、||
的執行順序一致。
其他接口
形如BiYYY類型接口
BiConsumer、BiFunction、BiPrediate
是**Consumer、Function、Predicate
** 的擴展,可以傳入多個參數,沒有 BiSupplier
是因爲 Supplier
沒有入參。
形如XXXYYY類型接口
如果用 XXX
來表示 int、long、double、boolean
,用 YYY
來表示 Consumer、Function、Predicate、Supplier
,那麼JDK8就內置了此四種基本數據類型的消費,構建,操作,斷言以及他們因此而產生的一系列其他接口,在此不再贅述。
UnaryOperator與BinaryOperator
- UnaryOperator:代表了一個作用於一個類型的操作,並且返回了同類型的結果
- BinaryOperator:代表了一個作用於於兩個同類型的操作,並且返回了同類型的結果
- XXXUnaryOperator:代表了一個作用於一個XXX數據類型的操作,並且返回了同類型數據的結果
- XXXBinaryOperator:代表了一個作用於兩個XXX數據類型的操作,並且返回了同類型數據的結果
ToXXXFunction與ToXXXBiFunction類型接口
- ToXXXFunction:接受一個輸入參數,返回一個XXX類型結果
- ToDoubleBiFunction:接受兩個輸入參數,返回一個XXX類型結果
ObjXXXConsumer類型接口
對一個對象和一個基本數據類型的消費行爲