Java8新特性(二) 之 函數式接口

FunctionalInterface

函數式接口

如果你的好奇心使你翻看Runnable接口源代碼,你會發現該接口被一個@FunctionalInterface的註解修飾,這是Java 8中添加的新註解,用於表示 函數式接口

FunctionalInterface-Runnable

函數式接口又是什麼鬼?

在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包中。如:

Functional-Package

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方法、兩個默認方法(composeandThen)和一個靜態方法identity
apply是接口的基本方法。
composeandThen是一對兒方法,他們的區別在於執行的順序不同。

//返回一個先執行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);方法,三個默認方法(andnegate,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 接口


求關注、分享、在看!!! 你的支持是我創作最大的動力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章