Java新特性-四大函數式接口


四大函數式接口指的是Consumer、Function、Predicate、Supplier,位於java.util.function包下:

函數式編程

lamabda表達式

函數式接口:在java中是指:有且僅有一個抽象方法的接口。也即適用於函數式編程場景的接口。而Java中的函數式編程體現就是Lambda,所以函數式接口就是可以適用於Lambda使用的接口。

lambda表達式表示函數式接口的實例

lambda表達式的類型是一個函數式接口類型。

備注:“語法糖"是指使用更加方便,但是原理不變的代碼語法。例如在遍歷集合時使用的for-each語法,其實底層的實現原理仍然是迭代器,這便是“語法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名內部類的“語法糖”,但是二者在原理上是不同的。

以下是這種函數式接口的示例:

@FunctionalInterface
interface Processor  {
    int  getStringLength(String str);
}

我們可以為其函數式接口實例賦值lambda表達式。

Processor stringProcessor = (String str) -> str.length();

函數式接口格式:接口中只能存在一個抽象方法

修飾符 interface 接口名稱{
    public abstract 返回值 方法名稱(參數列表)
    // 其他方式 
}
// public abstract 可以不寫 編譯器自動加上
修飾符 interface 接口名稱{
       返回值 方法名稱(參數列表)
    // 其他方式 
}

方法前面的public abstract 可以不寫,編譯器會自動加上

@FunctionalInterface注解

@FunctionalInterface // 標明為函數式接口
public abstract MyFunctionInterface{
    void method(); //抽象方法
}

一旦使用該注解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。需要注意的是,即使不使用該注解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣。(該接口是一個標記接口)

Javadoc關於函數式接口的規范

An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification.Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface’s abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
 
Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
 
If a type is annotated with this annotation type, compilers are required to generate an error message unless:
• The type is an interface type and not an annotation type, enum, or class.
• The annotated type satisfies the requirements of a functional interface.
 
However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a Functional Interface annotation is present on the interface declaration.

根據Java語言規范的定義,一個使用了該注釋的接口類型聲明將被視為一個函數式接口。從概念上講,一個函數式接口有且只有一個抽象方法。由於默認方法(default)已經有了實現,所以它們不是抽象方法

如果一個接口中聲明的抽象方法是重寫了超類Object類中任意一個public方法,那么這些抽象方法並不會算入接口的抽象方法數量中。

因為任何接口的實現都會從其父類Object或其它地方獲得這些方法的實現。
        注意:函數式接口的實現可以由Lambda表達式方法引用構造器引用等方式實現。
        如果一個類型使用了該注釋@FunctionalInterface,除非這個類型是一個接口類型(而不是一個注釋類型、枚舉或類),否則編譯器將會生成一個錯誤信息。同時使用該注釋的接口滿足函數式接口的要求,即一個函數式接口有且只有一個抽象方法。

        但是編譯器會將所有定義為函數式接口(滿足函數式接口要求)的接口視為函數式接口,而不管這個接口聲明中是否使用了函數式接口的注釋(即@FunctionalInterface)。
 從中我們可以知道:

  • 一個函數式接口有且只有一個抽象方法。
  • 默認方法不是抽象方法,因為它們已經實現了。
  • 重寫了超類Object類中任意一個public方法的方法並不算接口中的抽象方法。

比如Comparator接口,這是jdk當中的一個函數式接口:

Comparator的接口方法(含有大量的default方法):

含有兩個抽象方法(雖然沒有被顯示聲明為abstract):

compare(T o1,T o2),equals(Object obj)

Java8中的函數式接口定義是只有一個抽象方法的接口,但是這個Comparator中,好像是有多個抽象方法,為什么它也叫函數式接口呢?。

但是實際上只有一個:int compare(T o1,T o2)

defualt方法  默認方法有點類似靜態方法。

equals方法是Object的方法。所以雖然Comparator接口中有兩個抽象方法compare和equals,但equals並不算入接口中的抽象方法,所以Comparator接口還是滿足函數式接口的要求,Comparator接口是一個函數式接口。

使用Lambda進行傳參

假設有一個 方法使用該函數式接口作為參數,那么就可以使用Lambda進行傳參.如線程中的Runable接口

public class Test_Functional {
    // 定義一個含有函數式接口的方法
    public static void doSomthing(MyFunctionalInterface functionalInterface) {
        functionalInterface.method();//調用自定義函數式接口的方法
    }
    public static void main(String[] args) {
        //調用函數式接口的方法
        doSomthing(()->System.out.println("excuter lambda!"));
    }
}

@FunctionalInterface // 標明為函數式接口
public abstract MyFunctionInterface{
    void method(); //抽象方法
}

使用Lambda作為返回值

如果一個方法的返回值類型是一個函數式接口,那么就可以直接返回一個Lambda表達式

public class lambda_Comparator {
    //下面給出 lambda 以及實際替代的內部類寫法
    private static Comparator<String> newComparator(){
        return (a,b)->b.length()-a.length();
    }
    private static Comparator<String> newComparator1(){
        return new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.length()-a.length();    
            }
        };
    }
    public static void main(String[] args) {
        String[] array={"abc","ab","abcd"};
        System.out.println(Arrays.toString(array));
        Arrays.sort(array, newComparator1()); // 方式一
        Arrays.sort(array,(a,b)->b.length()-a.length());//更簡單的方式
        System.out.println(Arrays.toString(array));
    }
}

Arrays.sort(...)其中的一種參數形式如下:

sort(T[] a, Comparator<? super T> c)//根據指定的比較器引發的順序對指定的對象數組進行排序。 

常用函數式接口

在上面簡單介紹了一些Lambda表達式得好處與語法,我們知道使用Lambda表達式是需要使用函數式接口的,那么,豈不是在我們開發過程中需要定義許多函數式接口。

其實不然,java8其實已經為我們定義好了4類內置函數式接口,這4類接口其實已經可以解決我們開發過程中絕大部分的問題,只有一小部分比較特殊得情況需要我們自己去定義函數式接口,下面就簡單來學習一下java8內置得4大核心函數式接口。

Consumer<T>:消費型接口(void accept(T t))

/**
     * 消費型接口Consumer<T>
     */
    @Test
    public void test1 () {
        consumo(500, (x) -> System.out.println(x));
    }

    public void consumo (double money, Consumer<Double> c) {
        c.accept(money);
    }

以上為消費型接口,有參數,無返回值類型的接口。

Supplier<T>:供給型接口(T get())

  來看一個簡單得例子:

/**
     * 供給型接口,Supplier<T>
     */
    @Test
    public void test2 () {
        Random ran = new Random();
        List<Integer> list = supplier(10, () -> ran.nextInt(10));

        for (Integer i : list) {
            System.out.println(i);
        }
    }

    /**
     * 隨機產生sum個數量得集合
     * @param sum 集合內元素個數
     * @param sup
     * @return
     */
    public List<Integer> supplier(int sum, Supplier<Integer> sup){
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < sum; i++) {
            list.add(sup.get());
        }
        return list;
    }

上面就是一個供給類型得接口,只有產出,沒人輸入,就是只有返回值,沒有入參

Function<T, R>:函數型接口(R apply(T t))

  下面看一個簡單的例子:

/**
     * 函數型接口:Function<R, T>
     */
    @Test
    public void test3 () {
        String s = strOperar(" asdf ", x -> x.substring(0, 2));
        System.out.println(s);
        String s1 = strOperar(" asdf ", x -> x.trim());
        System.out.println(s1);
    }

    /**
     * 字符串操作
     * @param str 需要處理得字符串
     * @param fun Function接口
     * @return 處理之后得字符傳
     */
    public String strOperar(String str, Function<String, String> fun) {
        return fun.apply(str);
    }

上面就是一個函數型接口,輸入一個類型得參數,輸出一個類型得參數,當然兩種類型可以一致。

Predicate<T>:斷言型接口(boolean test(T t))

  下面看一個簡單得例子:

/**
     * 斷言型接口:Predicate<T>
     */
    @Test
    public void test4 () {
        List<Integer> l = new ArrayList<>();
        l.add(102);
        l.add(172);
        l.add(13);
        l.add(82);
        l.add(109);
        List<Integer> list = filterInt(l, x -> (x > 100));
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }

    /**
     * 過濾集合
     * @param list
     * @param pre
     * @return
     */
    public List<Integer> filterInt(List<Integer> list, Predicate<Integer> pre){
        List<Integer> l = new ArrayList<>();
        for (Integer integer : list) {
            if (pre.test(integer))
                l.add(integer);
        }
        return l;
    }

上面就是一個斷言型接口,輸入一個參數,輸出一個boolean類型得返回值。

其他類型的一些函數式接口

除了上述得4種類型得接口外還有其他的一些接口供我們使用:

    1).BiFunction<T, U, R> 

      參數類型有2個,為T,U,返回值為R,其中方法為R apply(T t, U u)

    2).UnaryOperator<T>(Function子接口)

      參數為T,對參數為T的對象進行一元操作,並返回T類型結果,其中方法為T apply(T t)

    3).BinaryOperator<T>(BiFunction子接口)

      參數為T,對參數為T得對象進行二元操作,並返回T類型得結果,其中方法為T apply(T t1, T t2)

    4).BiConsumer(T, U) 

      參數為T,U無返回值,其中方法為 void accept(T t, U u)

    5).ToIntFunction<T>、ToLongFunction<T>、ToDoubleFunction<T>

      參數類型為T,返回值分別為int,long,double,分別計算int,long,double得函數。

    6).IntFunction<R>、LongFunction<R>、DoubleFunction<R>

      參數分別為int,long,double,返回值為R。

以上就是java8內置得核心函數式接口,其中包括了大部分得方法類型,所以可以在使用得時候根據不同得使用場景去選擇不同得接口使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM