天天看點

Java8 新特性 函數式接口

什麼是函數式接口

  函數式接口是Java8引用的一個新特性,是一種特殊的接口:SAM類型的接口(Single Abstract Method)。但是它還是一個接口,隻是有些特殊罷了。  函數式接口的出現主要是為了配合Java8的另一個新特性Lamdba表達式來使用。

  • 接口中隻有一個抽象方法
  • 接口中面可以加一個注解@FunctionalInterface來檢查接口中的方法是不是隻有一個抽象方法
  • 在接口裡面可以加入 預設方法 和 靜态方法
  • 函數式接口也可以繼承,但是繼承的時候,抽象方法必須一樣
  • 函數式接口重寫父類的的方法,并不會計入到自己的抽象方法中

自定義函數式接口

//加入這個注解是為了檢測接口中是否符合函數式接口的要求
@FunctionalInterface
public interface MyFunctionInterction {
    //唯一的抽象方法
    void absoluMethod();

    //重寫Object的方法
    @Override
    String toString();

    //預設方法
    default void defaultMethod() {
        System.out.println("預設方法");
    }

    //靜态方法
    static void stativMethod() {
        System.out.println("靜态方法");
    }
}
           

函數式接口的簡單使用

裡面的預設方法可以直接使用
           
public class TestFunctionIntection {
    public static void main(String[] args) {
        TestFunctionIntection testFunctionIntection = 
                new TestFunctionIntection();
        testFunctionIntection.test(
                //Lamdba表達式的簡單使用
                () -> System.out.println("函數式接口裡面的抽象方法"));
    }

    /**
     * 自己定義的一個方法,并使用自定義的一個消費類型的函數式接口
     * @param myFunctionInterction
     */
    public void test(MyFunctionInterction 
                             myFunctionInterction) {

        //函數式接口裡面的抽象方法
        myFunctionInterction.absoluMethod();

        //預設方法
        myFunctionInterction.defaultMethod();

        //靜态方法
        MyFunctionInterction.stativMethod();
    }
}
           

java8裡面自定義的四個核心的函數式接口

上面我自定義的一個接口,就是一個消費類型的函數式接口。其實這類接口在java.util.function裡面有定義的,就是void Consumer< T >,消費類型接口,上面代碼中的test方法裡面的接口其實可以換成Consumer< T >接口,也可以用,下面主要就是介紹這四個函數式接口的簡單使用。
Java8 新特性 函數式接口

下面是這四個核心接口的簡單使用

public class FunctionTest {
    //Consumer<T> 消費型接口
    @Test
    public void test1() {
        Consumer<String> consumer = (x) -> System.out.println(x);
        consumer.accept("消費型接口,沒有傳回值!");
    }
    //輸出:消費型接口,沒有傳回值!

    //供給型接口
    @Test
    public void test2() {
        Supplier<String> supplier = () -> "主要的作用就是建立對象!";
        String s = supplier.get();
        System.out.println(s);
    }
    //輸出:主要的作用就是建立對象!

    //函數型接口
    //Function<T,R> T 接收的參數,R 傳回值類型
    @Test
    public void test3() {
        Function<Integer, String> function = (x) -> x + ":為String類型";
        String apply = function.apply(7);
        System.out.println(apply);
    }
    //輸出:7:為String類型    

    //斷言型接口
    @Test
    public void test4() {
        Predicate<Integer> predicate = (x) -> x > 10;
        boolean test = predicate.test(11);
        System.out.println(test);
    }
    //輸出:true
}
           

Consumer 的應用

//Consumer<T> 消費型接口
    @Test
    public void test1() {
        //定義一個消費型接口,隻輸出輸入的内容
        Consumer<String> consumer = (x) -> System.out.println(x);
        //在輸入的内容後面加上·--加上了預設方法·
        Consumer<String> consumer2 = (x) -> System.out.println(x + "--加上了預設方法");
        //執行順序  先執行 accept 後面執行 addThen(然後) 
        consumer.andThen(consumer2).accept("消費型接口,沒有傳回值!");
    }
    //輸出:消費型接口,沒有傳回值                     (accept輸出的值) 
    //輸出:消費型接口,沒有傳回值 !--加上了預設方法 (addThen輸出的值)
           

Consumer 的預設方法的源碼:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}
           

  其傳回值這一句是重點,先是傳入一個Consumer接口,然後傳回一個Consumer接口,說明可以用表達式鍊,然後用這個特性可以把資料進一次進行加工。

  (T t) -> { accept(t); after.accept(t); };這一句,傳回的順序首先是調用抽象方法,然後再調用預設方法,說明這個預設方法隻可以對資料進行再加工,不能再抽象方法前面。

Supplier 的應用

//供給型接口,這個方法若以用在工廠方法中
    @Test
    public void test2() {
        //跟據一個字元串建立對象
        Supplier<String> supplier = () -> "主要的作用就是建立對象!";
        //擷取一個對象
        String s = supplier.get();
        //擷取兩個以象
        String s1 = supplier.get();
        //兩個對象内容一樣
        System.out.println(s.equals(s1));
        System.out.println(s);
        //用方法引用的方式建立一個對象
        Supplier<SupplierTest> testSupplier = SupplierTest::new;
        //用new的方式建立一個對象
        Supplier<SupplierTest> supplierTestSupplier = () -> new SupplierTest("張三");
        //可以通過supplierTestSupplier 來擷取一個對象,并且可以調用裡面的方法
        String name = supplierTestSupplier.get().getName();
        System.out.println(name);
    }
    //輸出:true
    //輸出:主要的作用就是建立對象!
    //輸出:張三
           

  Supplier< T >接口類型就有一個方法簽名。T get()方法,沒有預設方法。

Function< T,R > 的應用

//預設主法addThen
//函數型接口
    //Function<T,R> T 接收的參數,R 傳回值類型
    @Test
    public void test3() {
        Function<String, String> f1 = (x) -> x +"+ ";
        Function<String, String> f2 = (x) -> x + "- ";
        //addThen(然後的意思)執行順序先執行f1,并且把執行後的結果作為f2的輸入參數
        String apply = f1.andThen(f2).apply("1");
        System.out.println(apply);
    }
    //輸出:1+ -
           

addThen的源碼:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}
           

源碼中最重要的一句

(V v) -> apply(before.apply(v)); 規定了執行順序
//預設主法compose
    //函數型接口
    //Function<T,R> T 接收的參數,R 傳回值類型
    @Test
    public void test3() {
        Function<String, String> f1 = (x) -> x +"+ ";
        Function<String, String> f2 = (x) -> x + "- ";
        //addThen(然後的意思)執行順序先執行f2,并且把執行後的結果作為f1的輸入參數
        String apply = f1.compose(f2).apply("1");
        System.out.println(apply);
    }
    //輸出:1- +
           

  三個預設方法,但是最後一個用的不多,這裡也就不再介紹了。

Predicate< T > 的應用

//默主方法negate 非
//斷言型接口
    @Test
    public void test4() {
        Predicate<Integer> predicate = (x) -> x > 10;
        boolean test = predicate.negate().test(11);
        System.out.println(test);
    }
    //輸出:false
           
//斷言型接口
    //預設方法 or 和 and
    @Test
    public void test4() {
        Predicate<Integer> p1 = (x) -> x > 10;
        Predicate<Integer> p2 = (x) -> x < 5;
        //預設方法 or 或
        boolean test = p1.or(p2).test(3);
        //預設方法 and 且
        boolean test2 = p1.and(p2).test(3);

        System.out.println(test);
        System.out.println(test2);
    }
    //輸出:true
    //輸出:false

           

函數式接口的使用

  函數式接口的的使用,大部分都是在流操作裡面進行,現在可以不太了解,但是可以在學習完流操作以後,再過來看,并且跟着寫一遍。代碼光看是沒有用的。如果不寫是不知道意思的。

參考的部落格:淺淺的函數式接口

我的公衆号

Java8 新特性 函數式接口

細節決定成敗!

個人愚見,如有不對,懇請斧正!

繼續閱讀