在上一篇Lambda的講解中咱們就提到過函數式接口,好比:Consumer<String>
consumer = (s) -> System.out.println(s);其中Consumer就是一個函數式接口。這裏是經過Lambda表達式建立了一個函數式接口的對象。若是不知道什麼是Lambda,請看《神祕的Lambda》。java
有且只有一個抽象方法的接口被稱爲函數式接口,函數式接口適用於函數式編程的場景,Lambda就是Java中函數式編程的體現,可使用Lambda表達式建立一個函數式接口的對象,必定要確保接口中有且只有一個抽象方法,這樣Lambda才能順利的進行推導。編程
與@Override 註解的做用相似,Java 8中專門爲函數式接口引入了一個新的註解:@FunctionalInterface 。該註解可用於一個接口的定義上,一旦使用該註解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,不然將會報錯。可是這個註解不是必須的,只要符合函數式接口的定義,那麼這個接口就是函數式接口。bash
實在不知道該在哪介紹這兩個方法了,因此就穿插在這裏了。app
java8中爲接口新增了一項功能,定義一個或者多個靜態方法。用法和普通的static方法同樣,例如:ide
public interface Interface {
/**
* 靜態方法
*/
static void staticMethod() {
System.out.println("static method");
}
}
複製代碼
注意:實現接口的類或者子接口不會繼承接口中的靜態方法。
函數式編程
java8在接口中新增default方法,是爲了在現有的類庫中中新增功能而不影響他們的實現類,試想一下,若是不增長默認實現的話,接口的全部實現類都要實現一遍這個方法,這會出現兼容性問題,若是定義了默認實現的話,那麼實現類直接調用就能夠了,並不須要實現這個方法。default方法怎麼定義?函數
public interface Interface {
/**
* default方法
*/
default void print() {
System.out.println("hello default");
}
}
複製代碼
注意:若是接口中的默認方法不能知足某個實現類須要,那麼實現類能夠覆蓋默認方法。不用加default關鍵字,
例如:post
public class InterfaceImpl implements Interface {
@Override
public void print() {
System.out.println("hello default 2");
}
}
複製代碼
在函數式接口的定義中是隻容許有一個抽象方法,可是能夠有多個static方法和default方法。學習
按照下面的格式定義,你也能寫出函數式接口:測試
@FunctionalInterface
修飾符 interface 接口名稱 {
返回值類型 方法名稱(可選參數信息);
// 其餘非抽象方法內容
}
複製代碼
雖然@FunctionalInterface註解不是必須的,可是自定義函數式接口最好仍是都加上,一是養成良好的編程習慣,二是防止他人修改,一看到這個註解就知道是函數式接口,避免他人往接口內添加抽象方法形成沒必要要的麻煩。
@FunctionalInterface
public interface MyFunction {
void print(String s);
}
複製代碼
看上圖是我自定義的一個函數式接口,那麼這個接口的做用是什麼呢?就是輸出一串字符串,屬於消費型接口,是模仿Consumer接口寫的,只不過這個沒有使用泛型,而是將參數具體類型化了,不知道Consumer不要緊,下面會介紹到,其實java8中提供了不少經常使用的函數式接口,Consumer就是其中之一,通常狀況下都不須要本身定義,直接使用就行了。那麼怎麼使用這個自定義的函數式接口呢?咱們能夠用函數式接口做爲參數,調用時傳遞Lambda表達式。若是一個方法的參數是Lambda,那麼這個參數的類型必定是函數式接口。例如:
public class MyFunctionTest {
public static void main(String[] args) {
String text = "試試自定義函數好使不";
printString(text, System.out::print);
}
private static void printString(String text, MyFunction myFunction) {
myFunction.print(text);
}
}
複製代碼
執行之後就會輸出「試試自定義函數好使不」這句話,若是某天需求變了,我不想輸出這句話了,想輸出別的,那麼直接替換text就行了。函數式編程是沒有反作用的,最大的好處就是函數的內部是無狀態的,既輸入肯定輸出就肯定。函數式編程還有更多好玩的套路,這就須要靠你們本身探索了。😝
<T>
:消費型接口抽象方法: void accept(T t),接收一個參數進行消費,但無需返回結果。
使用方式:
Consumer consumer = System.out::println;
consumer.accept("hello function");
複製代碼
默認方法: andThen(Consumer<? super T> after),先消費而後在消費,先執行調用andThen接口的accept方法,而後在執行andThen方法參數after中的accept方法。
使用方式:
Consumer<String> consumer1 = s -> System.out.print("車名:"+s.split(",")[0]);
Consumer<String> consumer2 = s -> System.out.println("-->顏色:"+s.split(",")[1]);
String[] strings = {"保時捷,白色", "法拉利,紅色"};
for (String string : strings) {
consumer1.andThen(consumer2).accept(string);
}
複製代碼
輸出: 車名:保時捷-->顏色:白色 車名:法拉利-->顏色:紅色
<T>
: 供給型接口抽象方法:T get(),無參數,有返回值。
使用方式:
Supplier<String> supplier = () -> "我要變的頗有錢";
System.out.println(supplier.get());
複製代碼
最後輸出就是「我要變得頗有錢」,這類接口適合提供數據的場景。
<T,R>
: 函數型接口抽象方法: R apply(T t),傳入一個參數,返回想要的結果。
使用方式:
Function<Integer, Integer> function1 = e -> e * 6;
System.out.println(function1.apply(2));
複製代碼
很簡單的一個乘法例子,顯然最後輸出是12。
默認方法:
使用方式:
Function<Integer, Integer> function1 = e -> e * 2;
Function<Integer, Integer> function2 = e -> e * e;
Integer apply2 = function1.compose(function2).apply(3);
System.out.println(apply2);
複製代碼
仍是舉一個乘法的例子,compose方法執行流程是先執行function2的表達式也就是33=9,而後在將執行結果傳給function1的表達式也就是92=18,因此最終的結果是18。
使用方式:
Function<Integer, Integer> function1 = e -> e * 2;
Function<Integer, Integer> function2 = e -> e * e;
Integer apply3 = function1.andThen(function2).apply(3);
System.out.println(apply3);
複製代碼
這裏咱們和compose方法使用一個例子,因此是如出一轍的例子,因爲方法的不一樣,執行順序也就不相同,那麼結果是大大不一樣的。andThen方法是先執行function1表達式,也就是32=6,而後在執行function2表達式也就是66=36。結果就是36。 **靜態方法:**identity(),獲取一個輸入參數和返回結果相同的Function實例。
使用方式:
Function<Integer, Integer> identity = Function.identity();
Integer apply = identity.apply(3);
System.out.println(apply);
複製代碼
日常沒有遇到過使用這個方法的場景,總之這個方法的做用就是輸入什麼返回結果就是什麼。
<T>
: 斷言型接口抽象方法: boolean test(T t),傳入一個參數,返回一個布爾值。
使用方式:
Predicate<Integer> predicate = t -> t > 0;
boolean test = predicate.test(1);
System.out.println(test);
複製代碼
當predicate函數調用test方法的時候,就會執行拿test方法的參數進行t -> t > 0的條件判斷,1確定是大於0的,最終結果爲true。
默認方法:
使用方式:
Predicate<String> predicate1 = s -> s.length() > 0;
Predicate<String> predicate2 = Objects::nonNull;
boolean test = predicate1.and(predicate2).test("&&測試");
System.out.println(test);
複製代碼
使用方式:
Predicate<String> predicate1 = s -> false;
Predicate<String> predicate2 = Objects::nonNull;
boolean test = predicate1.and(predicate2).test("||測試");
System.out.println(test);
複製代碼
使用方式:
Predicate<String> predicate = s -> s.length() > 0;
boolean result = predicate.negate().test("取反");
System.out.println(result);
複製代碼
很明顯正常執行test方法的話應該爲true,可是調用negate方法後就返回爲false了。 **靜態方法:**isEqual(Object targetRef),對當前操做進行"="操做,即取等操做,能夠理解爲 A == B。
使用方式:
boolean test1 = Predicate.isEqual("test").test("test");
boolean test2 = Predicate.isEqual("test").test("equal");
System.out.println(test1); //true
System.out.println(test2); //false
複製代碼
BiConsumer、BiFunction、BiPrediate 是 Consumer、Function、Predicate 的擴展,能夠傳入多個參數,沒有 BiSupplier 是由於 Supplier 沒有入參。
IntConsumer、IntFunction、IntPredicate、IntSupplier、LongConsumer、LongFunction、LongPredicate、LongSupplier、DoubleConsumer、DoubleFunction、DoublePredicate、DoubleSupplier。 其實經常使用的函數式接口就那四大接口Consumer、Function、Prediate、Supplier,其餘的函數式接口就不一一列舉了,有興趣的能夠去java.util.function這個包下詳細的看。
你們看后辛苦點個贊點個關注哦!後續還會後更多的博客。想來想去建了個羣,你們能夠掃碼加羣,一塊兒學習、共同進步。有興趣能夠掃碼加羣。若有錯誤,煩請指正。
![]()