1. 程式人生 > >java程式碼之美(15)---Java8 Function、Consumer、Supplier

java程式碼之美(15)---Java8 Function、Consumer、Supplier

Java8 Function、Consumer、Supplier

有關JDK8新特性之前寫了三篇部落格:

1、java程式碼之美(1)---Java8 Lambda

2、java程式碼之美(2)---Java8 Stream

3、java程式碼之美(13)--- Predicate詳解

這一篇我們來了解JDK8已經定義好的幾個函式式介面。

一、概述

Jdk8之後新增的一個重要的包 : java.util.function

該包下所有的介面都是函式式介面, 按分類主要分為四大介面型別: FunctionConsumerPredicateSupplier。有關Predicate這裡不再講解,因為上面有單獨寫過一篇部落格。

延伸如下

這裡也僅僅是展示一部分,我們看看java.util.function包下


二、Consumer

作用 一聽這名字就知道是消費某個物件,沒有返回值。

1、原始碼

在原始碼中只有兩個方法,一個抽象方法,一個預設方法。

@FunctionalInterface
public interface Consumer<T> {

    /**
     * 抽象方法:傳入一個指定泛型的引數,無返回值
     */
    void accept(T t);

    /**
     * 如同方法名字一樣andThen,類似一種相加的功能(下面會舉例說明)
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

2、使用示例

    public static void main(String[] args) {
        testConsumer();
        testAndThen();
    }
    /**
     * 一個簡單的平方計算
     */
    public static void testConsumer() {
        //設定好Consumer實現方法
        Consumer<Integer> square = x -> System.out.println("平方計算 : " + x * x);
        //傳入值
        square.accept(2);
    }
    /**
     * 定義3個Consumer並按順序進行呼叫andThen方法
     */
    public static void testAndThen() {
        //當前值
        Consumer<Integer> consumer1 = x -> System.out.println("當前值 : " + x);
        //相加
        Consumer<Integer> consumer2 = x -> { System.out.println("相加 : " + (x + x)); };
        //相乘
        Consumer<Integer> consumer3 = x -> System.out.println("相乘 : " + x * x);
        //andThen拼接
        consumer1.andThen(consumer2).andThen(consumer3).accept(1);
    }

執行結果

單個這樣消費看去並沒啥意義,但如果是集合操作就有意義了,所以Jdk8的Iterator介面就引入了Consumer。

3、JDK8使用

Iterable介面的forEach方法需要傳入Consumer,大部分集合類都實現了該介面,用於返回Iterator物件進行迭代。

public interface Iterable<T> {
    //forEach方法傳入的就是Consumer
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

我們在看給我們帶來的便利

    public static void main(String[] args) {
        //假設這裡有個集合,集合裡的物件有個status屬性,現在我想對這個屬性賦值一個固定值
        List<Pension> pensionList = new ArrayList<>();
        //1、傳統的通過for迴圈新增
        for (Pension pension : pensionList) {
            pension.setStatus(1);
        }
        //2、通過forEach的Consumer新增
        pensionList.forEach(x -> x.setStatus(1));
    }

這樣一比較是不是程式碼簡潔了點,這就是Consumer是我們程式碼帶來簡潔的地方。


三、Supplier

作用 提前定義可能返回的一個指定型別結果,等需要呼叫的時候再獲取結果。

1、原始碼

@FunctionalInterface
public interface Supplier<T> {

    /**
     * 只有這一個抽象類
     */
    T get();
}

原始碼非常簡單。

2、JDK8使用

在JDK8中Optional物件有使用到

Optional.orElseGet(Supplier<? extends T>) //當this物件為null,就通過傳入supplier建立一個T返回。

我們看下原始碼

 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

使用示例

  public static void main(String[] args) {
        Person son = null;
        //先判斷son是否為null,如果為不為null則返回當前物件,如果為null則返回新建立的物件
        BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());

    }

這樣程式碼是不是又簡單了。有關Optional這裡就不多說,接下來會單獨寫一篇部落格。


四、Function

作用 實現一個”一元函式“,即傳入一個值經過函式的計算返回另一個值。

1、原始碼

    @FunctionalInterface
    public interface Function<T, R> {
        
        /**
         * 抽象方法: 根據一個數據型別T加工得到一個數據型別R
         */
        R apply(T t);

        /**
         * 組合函式,呼叫當前function之前呼叫
         */
        default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }

        /**
         * 組合函式,呼叫當前function之後呼叫
         */
        default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }

        /**
         *  靜態方法,返回與原函式引數一致的結果。x=y
         */
        static <T> java.util.function.Function<T, T> identity() {
            return t -> t;
        }
    }

2、使用示例

public static void main(String[] args) {
        applyTest();
        andThenTest();
        composeTest();
        test();
    }

    /**
     * 1、apply 示例
     */
    private static void applyTest() {
        //示例1:定義一個funciton,實現將String轉換為Integer
        Function<String, Integer> function = x -> Integer.parseInt(x);
        Integer a = function.apply("100");
        System.out.println(a.getClass());
        // 結果:class java.lang.Integer
    }

    /**
     * 2、andThen 示例
     */
    private static void andThenTest() {
        //示例2:使用andThen() 實現一個函式 y=10x + 10;
        //先執行 10 * x
        Function<Integer, Integer> function2 = x -> 10 * x;
        //通過andThen在執行 這裡的x就等於上面的10 * x的值
        function2 = function2.andThen(x -> x + 10);
        System.out.println(function2.apply(2));
        //結果:30

    }

    /**
     * 3、compose 示例
     */
    private static void composeTest() {
        //示例3:使用compose() 實現一個函式 y=(10+x)2;
        Function<Integer, Integer> function3 = x -> x * 2;
        //先執行 x+10 在執行(x+10)*2順序與上面相反
        function3 = function3.compose(x -> x + 10);
        System.out.println(function3.apply(3));
        //結果:26
    }

    /**
     * 4、綜合示例
     */
    private static void test() {

//示例4:使用使用compose()、andThen()實現一個函式 y=(10+x)*2+10;
        //執行第二步
        Function<Integer, Integer> function4 = x -> x * 2;
        //執行第一步
        function4 = function4.compose(x -> x + 10);
        //執行第三步
        function4 = function4.andThen(x -> x + 10);
        System.out.println(function4.apply(3));
       //結果:36

    }

3、JDK8使用

有兩個地方很常用

1、V HashMap.computeIfAbsent(K , Function<K, V>) // 簡化程式碼,如果指定的鍵尚未與值關聯或與null關聯,使用函式返回值替換。
2、<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 轉換流

computeIfAbsent使用示例

Map<String, List<String>> map = new HashMap<>();
List<String> list;

// java8之前寫法
list = map.get("key");
if (list == null) {
    list = new LinkedList<>();
    map.put("key", list);
}
list.add("11");

// 使用 computeIfAbsent 可以這樣寫 如果key返回部位空則返回該集合 ,為空則建立集合後返回
list = map.computeIfAbsent("key", k -> new ArrayList<>());
list.add("11");

stream中map使用示例

  public static void main(String[] args) {
        List<Person> persionList = new ArrayList<Person>();
        persionList.add(new Person(1,"張三","男",38));
        persionList.add(new Person(2,"小小","女",2));
        persionList.add(new Person(3,"李四","男",65));

        //1、只取出該集合中所有姓名組成一個新集合(將Person物件轉為String物件)
        List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList());
        System.out.println(nameList.toString());
        }

程式碼是不是又簡潔了。

總結 這些函式式介面作用在我看來,就是定義一個方法,方法中有個引數是函式式介面,這樣的話函式的具體實現則由呼叫者來實現。這就是函式式介面的意義所在。

一般我們也會很少去定義一個方法,方法引數包含函式介面。我們更重要的是學會使用JDk8中帶有函式式介面引數的方法,來簡化我們的程式碼。

參考

1、JDK1.8函式式介面Function、Consumer、Predicate、Supplier

2、JAVA 8 函式式介面




你如果願意有所作為,就必須有始有終。(25)