函數式接口
如果你的好奇心使你翻看Runnable
接口源代碼,你會發現該接口被一個@FunctionalInterface
的註解修飾,這是Java 8中添加的新註解,用於表示 函數式接口。
函數式接口又是什麼鬼?
在Java 8中,把那些僅有一個抽象方法的接口稱爲函數式接口。如果一個接口被@FunctionalInterface
註解標註,表示這個接口被設計成函數式接口,只能有一個抽象方法,如果你添加多個抽象方法,編譯時會提示“Multiple non-overriding abstract methods found in interface XXX”之類的錯誤。
標註爲FunctionalInterface的接口被稱爲函數式接口,該接口只能有一個自定義方法,但是可以包括從object類繼承而來的方法。如果一個接口只有一個方法,則編譯器會認爲這就是一個函數式接口。是否是一個函數式接口,需要注意的有以下幾點:
- 該註解只能標記在”有且僅有一個抽象方法”的接口上。
- JDK8接口中的靜態方法和默認方法,都不算是抽象方法。
- 接口默認繼承java.lang.Object,所以如果接口顯示聲明覆蓋了Object中方法,那麼也不算抽象方法。
- 該註解不是必須的,如果一個接口符合”函數式接口”定義,那麼加不加該註解都沒有影響。加上該註解能夠更好地讓編譯器進行檢查。如果編寫的不是函數式接口,但是加上了@FunctionInterface,那麼編譯器會報錯。
- 在一個接口中定義兩個自定義的方法,就會產生Invalid ‘@FunctionalInterface’ annotation; FunctionalInterfaceTest is not a functional interface錯誤.
函數式方法又能做什麼?
Java8允許你以Lambda表達式的方式爲函數式接口提供實現,通俗的說,你可以將整個Lambda表達式作爲接口的實現類。
除了Runnable
之外,Java 8中內置了許多函數式接口供開發者使用,這些接口位於java.util.function
包中。如:
name | type | result | desc |
---|---|---|---|
Consumer | Consumer | T -> void | 接收T對象,不返回值 |
Predicate | Predicate | T -> boolean | 接收T對象並返回boolean |
Function | Function<T, R> | T -> R | 接收T對象,返回R對象 |
Supplier | Supplier | void -> T | 提供T對象(例如工廠),不接收值 |
UnaryOperator | UnaryOperator | T -> T | 接收T對象,返回T對象 |
BinaryOperator | BinaryOperator | T, T -> T | 接收兩個T對象,返回T對象 |
如果輸入參數是基本類型,爲了避免自動拆箱裝箱,可以使用其他基本類型的函數接口。
Function
interface Function<T, R>
接口包含一個apply
方法、兩個默認方法(compose
、andThen
)和一個靜態方法identity
。
apply
是接口的基本方法。
compose
、andThen
是一對兒方法,他們的區別在於執行的順序不同。
//返回一個先執行before函數對象apply方法再執行當前函數對象apply方法的函數對象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//返回一個先執行當前函數對象apply方法再執行after函數對象apply方法的函數對象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
// 根據上面的解釋想一下執行結果
Function<Integer, Integer> name = e -> e * 2;
Function<Integer, Integer> square = e -> e * e;
int value = name.andThen(square).apply(3);
int value2 = name.compose(square).apply(3);
//返回一個執行了apply()方法之後只會返回輸入參數的函數對象
Object identity = Function.identity().apply("Test");
Consumer
interface Consumer<T>
接口包含一個void accept(T t);
方法、默認方法andThen
.
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
只有一個默認方法也是和它的返回類型有關係,因爲返回的是void。
Predicate
interface Predicate<T>
接口包含一個boolean test(T t);
方法,三個默認方法(and
,negate
,or
),還有一個靜態方法。
我們也知道Predicate接口是返回boolean類型的,所以一看就知道是條件判斷的。
舉幾個栗子吧:
String name = "hello";
Predicate<String> predicate = x -> x.equals("hello");
Predicate<String> predicate2 = x -> x.length() < 2;
// and 多個Predicate條件並的關係判斷,第一個爲false就不往下走了,滿足短路原則
System.out.println(predicate.and(predicate2).test(name));
// or 多個Predicate條件或的關係判斷,同樣滿足短路原則
System.out.println(predicate.or(predicate2).test(name));
// negate 取反的意思,就是否的條件判斷
System.out.println(predicate.and(predicate2.negate()).test(name));
// isEqual 靜態方法,判斷是否相等
System.out.println(Predicate.isEqual(name).test(name));
Supplier
Supplier
只有一個get()方法。
我們來看看幾個栗子:
Supplier<String> supplier = () -> "hello world";
//get方法不接受參數,返回一個結果
System.out.println("supplier = [" + supplier.get() + "]");
//替代不接受參數的工廠方法
Supplier<Student> studentSupplier = () -> new Student();
System.out.println(studentSupplier.get());
//因爲Student的構造方法不接受參數,返回一個結果,符合Supplier接口的要求,可以簡寫如下:
Supplier<Student> studentSupplier2 = Student::new;
總結
函數式接口其實差別不大,只是參數和返回的不同,只要想明白其中的一種,其他的也就懂了。
參考
JDK8新特性-java.util.function-Function接口
Java8的一些新特性 java.util.function包
Java JVM(七):Function,Consumer,Predicate 接口