Java8新特性之常用的函數式接口----Function,Consumer,Predicate,Supplier

一.什麼是函數式接口

最近在開發過程中,發現許多Java8新特性的應用及其廣泛,很多代碼如果不瞭解這些知識是看不懂的,所以打算總結一下。


  • 函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。
  • 函數式接口可以被隱式轉換爲 lambda 表達式

lambda表達式介紹: lambda表達式介紹

代碼示例:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

目前個人理解:函數式接口其實就是一種接口而已,爲什麼它如此特殊是因爲它只有一個抽象方法,方便我們用Lambda表達式等方式來寫代碼

比如上面的接口可以這樣使用:

GreetingService greetService1 = message -> System.out.println("Hello " + message);


二.四大函數式接口

java.util.function 它包含了很多類,用來支持 Java的 函數式編程,其中有許多函數式接口,但是比較基本比較常用的有四種,分別是Function,Consumer,Predicate,Supplier。下面就讓我們一起來學習一下

附常見的泛型類型:

使用大寫字母A,B,C,D......X,Y,Z定義的,就都是泛型,把T換成A也一樣,這裏T只是名字上的意義而已
 
? 表示不確定的java類型
T (type) 表示具體的一個java類型
K V (key value) 分別代表java鍵值中的Key Value
E (element) 代表Element

1.Function功能型函數式接口

表示接受一個參數併產生結果的函數

@FunctionalInterface
public interface Function<T, R> {

    /**
     * 接收一個參數,進行操作後將結果返回
     */
    R apply(T t);

    /**
     * 返回一個 先執行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));
    }

    /**
     * 返回一個執行了apply()方法之後只會返回輸入參數的函數對象
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

可以看出來Function接口的核心就是apply這個方法。

代碼示例:

public class FunctionDemo {

    static int calValue(int value, Function<Integer, Integer> function) {
        return function.apply(value);
    }


    public static void main(String[] args) {

        int value = 10;
        //lambda表達式
        int res = calValue(value, (x) -> {return value + 10;});
        System.out.println(res);
    }
}

calValue作爲類的靜態方法,傳入一個Function接口作爲參數,返回值是經過函數計算的結果。在main方法中用lambda表達式實現了這個接口,這樣讓函數的擴展性更強了。


2.Predicate斷言型函數式接口

接受一個輸入參數,返回一個布爾值結果。

@FunctionalInterface
public interface Predicate<T> {

    /**
     * 評估當前參數
     */
    boolean test(T t);

    /**
     * 對應了java的連接符號&&
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 對應了java的連接符號!
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 對應了java的連接符號||
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

除了主要的抽象方法test外,另外三個方法其實都是用於邏輯判斷的,可以讓我們對斷言進行排列組合,方便編程

int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        List<Integer> list=new ArrayList<>();
        for(int i:numbers) {
            list.add(i);
        }
        Predicate<Integer> p1=i->i>5;
        Predicate<Integer> p2=i->i<20;
        Predicate<Integer> p3=i->i%2==0;
        List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
        System.out.println(test.toString());
/** print:[6, 8, 10, 12, 14]*/

需求是輸出偶數,只需要通過stream流式編程然後定義三個Predicate進行過濾即可。


3.Consumer消費型函數式接口

代表了 接受一個輸入參數並且無返回的操作

    public static void modifyTheValue(int value, Consumer<Integer> consumer) {
        consumer.accept(value);
    }
v
    public static void main(String[] args) {
        // (x) -> System.out.println(x * 2)接受一個輸入參數x
        // 直接輸出,並沒有返回結果
        // 所以該lambda表達式可以實現Consumer接口
        modifyTheValue(3, (x) -> System.out.println(x * 2));
    }

輸出結果爲6


4.Supplier供給型函數式接口

無參數,返回一個結果

    public static String supplierTest(Supplier<String> supplier) {  
        return supplier.get();  
    }  

    public static void main(String args[]) {
        String name = "冷冷";
        // () -> name.length() 無參數,返回一個結果(字符串長度)
        // 所以該lambda表達式可以實現Supplier接口
        System.out.println(supplierTest(() -> name.length() + ""));
    }

三.總結

函數式接口爲Java賦予了函數式編程的能力。

“函數式編程"是一種"編程範式”(programming paradigm),也就是如何編寫程序的方法論。它屬於"結構化編程"的一種,主要思想是把運算過程儘量寫成一系列嵌套的函數調用,有許多的好處。

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