1. 程式人生 > >Java8 java.util.function包解析(Function,Consumer,Predicate,Supplier)

Java8 java.util.function包解析(Function,Consumer,Predicate,Supplier)

Java8的工具包新增了function包,裡面主要包含了Function,Consumer,Predicate,Supplier四種類。
一、Function類
Function類包含四種方法,其中一個抽象方法apply(),兩個default方法andThen()和compose(),以及一個靜態方法identity()。
例項化Function的時候需要實現其中的apply()方法,apply方法接收一個模板型別作為輸入引數,在andThen()和compose()方法裡會自動呼叫apply()方法。

andThen方法接收一個Function類的例項,通過andThen可以將任意多個Function的apply方法呼叫連線起來,在Java8裡的原始碼如下:

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

測試用例:

Function<String, String> function1 = string -> {
    System.out.println("function1輸出了: " + string
); return string; }; Function<String, String> function2 = string -> { System.out.println("function2輸出了: " + string); return string; }; function1.andThen(function2).apply("hello world");

輸出結果

function1輸出了: hello world
function2輸出了: hello world

可以看到呼叫順序是先呼叫funtion1然後呼叫function2。

compose方法和andThen方法一樣接收一個另一個Function作為引數,但是順序與andThen恰恰相反。
在Java8裡的原始碼如下:

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

接下來是測試用例,保持前面的不變,只把最後一句由andThen改成

function1.compose(function2).apply("hello world");

輸出結果:

function2輸出了: hello world
function1輸出了: hello world

可以看到這次先執行了function2之後再執行function1。

identity方法是一個靜態方法,作用是返回一個Function物件,返回的物件總是返回它被傳入的值。
原始碼很簡單

static <T> Function<T, T> identity() {
    return t -> t;
}

二、Consumer類
Consumer類包含兩個方法,一個accept方法用來對輸入的引數進行自定義操作,因為是個抽象方法,所以需要例項化物件的時候進行Override,另一個andThen方法跟Function的方法一樣是一個default方法,已經有內部實現所以不需要使用者重寫,並且具體功能也跟Function差不多。Consumer的中文意思是消費者,意即通過傳遞進一個引數來對引數進行操作。
示例程式碼:

import java.util.function.Consumer;

public class Test {
    public static void main(String[] args) {
        Foo f = new Foo();
        f.foo(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
            }
        });
    }
}

class Foo {
    private int[] data = new int[10];

    public Foo() {
        for(int i = 0; i < 10; i++) {
            data[i] = i;
        }
    }

    public void foo(Consumer<Integer> consumer) {
        for(int i : data)
            consumer.accept(i);
    }
}

首先新建一個類,在這個類裡有一個int型陣列,在構造方法裡對陣列賦初值,然後又一個foo方法傳入一個Consumer物件,對每一個數組項呼叫consumer物件的accept方法。在main函式裡實例化Foo物件並呼叫foo方法。
輸出結果:

0
1
2
3
4
5
6
7
8
9

在上面的程式碼中,由於Java8引入的lambda表示式,所以其中的

        f.foo(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
            }
        });

可以簡寫成

        f.foo(integer -> System.out.println(integer));

或者進一步簡寫成

        f.foo(System.out::println);

三、Predicate類
Predicate類包含5個方法,最重要的是test方法,這是一個抽象方法,需要程式設計者自己去Override,其他的三個default方法裡都使用到了這個方法,這三個方法分別是and方法,negate方法和or方法,其中and和or方法與前面兩個類的andThen方法類似,這兩個方法都接受另一個Predicate物件作為引數,and方法返回這兩個物件分別呼叫test方法之後得到的布林值的並,相當於predicate1.test() && predicate2.test(),or方法返回這兩個物件分別呼叫test方法之後得到的布林值的或,相當於predicate1.test() || predicate2.test()。

示例程式碼:

        Predicate<Integer> predicate1 = new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer <= 0;
            }
        };
        Predicate<Integer> predicate2 = new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer > 0;
            }
        };
        System.out.println("and: " + predicate1.and(predicate2).test(1));
        System.out.println("or: " + predicate1.or(predicate2).test(1));
        System.out.println("negate: " + predicate1.negate().test(1));

輸出結果:

and: false
or: true
negate: true

同樣,可以簡化成lambda表示式

        Predicate<Integer> predicate1 = integer -> integer <= 0;
        Predicate<Integer> predicate2 = integer -> integer > 0;

四、Supplier類
supplier的中文意思是提供者,跟Consumer類相反,Supplier類用於提供物件,它只有一個get方法,是一個抽象方法,需要程式設計者自定義想要返回的物件。
示例程式碼:

        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return new Random().nextInt(100);
            }
        };

        int[] ints = new int[10];
        for(int i = 0; i < 10; i++) {
            ints[i] = supplier.get();
        }
        Arrays.stream(ints).forEach(System.out::println);

首先自定義了一個Supplier物件,對於其get方法,我每次都返回一個100以內的隨機數,並在之後利用這個物件給一個長度為10的int陣列賦值並輸出。

輸出結果:

49
27
14
46
8
97
20
82
75
30

總結:
util裡的function包裡並不僅僅只有這四個類,只是其中絕大部分都是由這四種衍生而來的,這個包主要是用於實現Java8最大的特性函數語言程式設計,所以在很多其他的包的一些類中的很多地方都用到了這個function包裡的類,更多的情況是作為方法中的一個匿名物件引數來使用,配合簡潔的lambda表示式使得程式可讀性變得更好。
最近正在學習Java的API,看到網上跟function包相關的知識比較少,所以自己通過了一天的嘗試了之後記錄總結了一下,肯定還有很多錯誤,畢竟接觸Java的時間不長,以後還會回來修改完善。