天天看點

jdk8函數接口

java8 新特性推出的 Lambda 表達式,即函數式程式設計,相信很多開發胸弟都會使用了,但是什麼是函數式程式設計呢?别問我,我也不知道标準的定義。其核心思想是:使用不可變值和函數,函數對一個值進行處理,映射成另一個值。

函數接口

java8之前接口類隻有方法的定義,沒有實作的,Java8對接口提供預設方法的新特性。一個接口類可以定義n個抽象方法,但如果有 @FunctionalInterface 注解修飾就不一樣了,該注釋會強制編譯檢查一個接口是否符合函數接口的标準。如果該注釋添加給一個枚舉類型、類或另一個注釋,或者接口包含不止一個抽象方法,編譯就會報錯。@FunctionalInterface 注解修飾的接口就是被定義成函數接口。

常用的函數接口

平時開發中常用的函數接口有無傳回值的Consumer,傳回值為Boolean的Predicate,把入參T映射成R傳回值的Function 和傳回執行個體對象的Supplier。接下來我們一起分析這四個函數接口類的源碼以及簡單的使用,先建立一個後面需要用到的實體類 Griez。

public class Griez implements Serializable {
    private int age;
    private String name;
    public Griez(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public Griez setAge(int age) {
        this.age = age;
        return this;
    }

    public String getName() {
        return name;
    }

    public Griez setName(String name) {
        this.name = name;
        return this;
    }
}           

複制

定義一個法國球星格子(我最喜歡的球員)類,為了簡單這裡隻有名字和年齡,他的能力值和薪資就不展示出來了,怕吓到大家。

Consumer

@FunctionalInterface
public interface Consumer<T> {
        // 執行時需要調用傳入的實作
    void accept(T t);

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

複制

Consumer函數接口有兩個方法,抽象方法accept需要調用者自己實作;andThen預設方法封裝了兩個Consumer執行順序的Consumer執行個體,先執行本執行個體的accept,再執行參數after的accept方法。
public static void main(String[] args) {
        Consumer<Griez> consumer = griez -> System.out.println(griez.getName() + griez.age);
        Consumer<Griez> afterConsumer = griez -> System.out.println(griez.getAge());
        Consumer<Griez> then = consumer.andThen(afterConsumer);
        then.accept(new Griez(29, "wolf"));
    }           

複制

第1,2行建立Consumer執行個體,并實作了accept方法。記得剛畢業出來面試有個面試官問我接口能不能建立執行個體,我給他的回答是:“你說呢?”。

Predicate

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
        // 兩個條件必須滿足
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    // 傳說中的 邏輯非 判斷
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    // 邏輯或判斷
    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 方法需要自定義邏輯,傳回一個boolean值,該函數接口是做判斷用途的。and 方法是兩個Predicate 邏輯與判斷;negate 方法是邏輯非判斷;or 方法是兩個 Predicate 邏輯或判斷;isEqual 方法是判斷targetRef是否與本執行個體相同。
public static void main(String[] args) {
    Predicate<Griez> predicate= griez -> griez.getAge() > 18;
    System.out.println("Griez已經成年了 : " + predicate.test(new Griez(29, "griez")));
}           

複制

test傳回Boolean值。

Function

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

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

複制

apply 方法接收一個T參數,然後一個R,該方法是類型映射作用。compose 方法是組裝了兩個Function執行順序行為Function對象,先執行參數的Function在執行本執行個體Function。andThen 跟 compose執行順序相反。identity 方法是傳回參數本身。
public static void main(String[] args) {
    Function function = o -> new Griez(20, "Female");
    System.out.println(function.apply(new Griez(29, "griez").getName()));
}           

複制

一個大老爺進去變成一個年輕小姐姐出來,變态的程式員。

Supplier

@FunctionalInterface
public interface Supplier<T> {
    T get();
}           

複制

該函數接口看上去就比較寒顫了,隻有一個方法。get 方法作用如名字一個,伸手黨來的,擷取一個執行個體。
public static void main(String[] args) {
    Supplier<Griez> supplier = () -> new Griez(29, "griez");
    System.out.println(supplier.get());
}           

複制

擷取一個Griez對象。