函數式接口

函數式接口

jdk1.8 之前的函數式接口

  • java.lang.Runnable
new Thread(()->System.out.println("開啓一個線程!")).start();
  • java.util.concurrent.Callable
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(()->"創建了一個線程");
  • java.security.PrivilegedAction
AccessController.doPrivileged((PrivilegedAction<Object>) ()->null);
  • java.util.Comparator
Collections.sort(list, (Comparator<Integer>) (o1, o2) -> o1.compareTo(o2));
  • java.io.FileFilter
File file = new File(rootPath);
File[] files = file.listFiles(var1->true);
  • java.awt.event.ActionListener
JButton button1 = new JButton("按鈕");
button1.addActionListener(e->System.out.println("你點擊了按鈕!"));

類似的接口還有如下:

  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • javax.swing.event.ChangeListener

使用方式類似,不在此贅述。

jdk1.8 新增的函數式接口

jdk1.8 內置的函數式接口放在包 java.util.function 下,默認在jdk安裝路徑下的 src.zip 中。

這些接口,主要分4大類:

  • Consumer(類似於消費者需要傳入參數無返回值)
  • Supplier(類似於生產者不需要傳入參數,但有返回值)
  • Function(有輸入也有返回)
  • Predicate(判斷函數,有輸入也有返回,返回trueor false)

他們特點如下:

接口 參數類型 返回類型 方法 用途
Consumer T void void accept(T t) 對類型T參數操作,無返回結果
Supplier T T get() 創造T類型參數
Function T R R apply(T t) 對類型T參數操作,返回R類型參數
Predicate T boolean boolean test(T t) 斷言型接口,對類型T進行條件篩選操作

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 的作用僅僅是傳入一個參數,並對其進行操作,它在本質上和無返回值的單參方法是一樣的:

public class Test {
    private void test(double money, Consumer<Double> con){
        con.accept(money);
    }

    @org.junit.Test
    public void test1(){
        double money = 10000;
        test(money,m->System.out.println("這次消費了" + m + "元!"));
        System.out.println(money);
    }
}

其結果如下:
在這裏插入圖片描述

由此可見,Consumer 的作用僅僅只是對傳入的參數進行操作,並不是真正的“消費掉了”,因爲函數式編程推崇的是不可變對象,Lambda表達式不允許修改“值”。

但Consumer的真正意義並不是簡單的實現一個無返回值的單參數方法(或者說行爲),而是由它的default方法所帶來的嵌套調用(連鎖調用),這種調用是無限次的。

public class Test {
    private void test(double money, Consumer<Double> con){
        con.accept(money);
    }

    @org.junit.Test
    public void test1(){
        double money = 10000;
        Consumer consumer = m->System.out.println("這次消費了"+m+"元");
        Consumer consumer1 = s->System.out.println("這次消費了"+s+"元");
        test(money,consumer.andThen(consumer1.andThen(consumer)));
        System.out.println(money);
    }
}

執行結果如下:
在這裏插入圖片描述

這裏將函數式編程的一大理念——對行爲的抽象,展示的淋漓盡致。

Supplier 接口

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

Supplier 的真正作用,也僅僅是爲產生對象而生,

@Test
    public  void test2(){
        System.out.println(((Supplier)()->(int)(Math.random()*100)).get());
    }

上述的測試用例僅僅是產生了一個100以內的整型隨機數,但這毫無意義,完全可以簡化成:

System.out.println((int)(Math.random()*100));

但這是兩種編程思想的問題,Supplier提供的是一種行爲,將行爲規範化,爲的是如果其他地方需要產生一個對象(比如100以內的整型,這當然可以直接傳入對象,但如果是一個複雜對象,這個對象的創建過程特別複雜),Supplier可以爲其提供一種產生該對象的規範行爲並直接可作爲參數傳入。

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;
    }
}

Function ,顧名思義,接受一個參數,經過處理後,再返回一個結果,其中 andThen() 方法接受一個行爲,並將父方法處理過的結果作爲參數再處理,而 compose() 方法正好相反,它是先自己處理然後將結果作爲參數傳給父方法執行,雖然處理的都是數據,但傳入的卻是行爲。

@Test
    public  void test3(){
        Function function = str->str+"suffix";
        Function function1 = str->str+"prefix";
        System.out.println(function.apply("111"));
        System.out.println(function.andThen(function1).apply("111"));
        System.out.println(function.compose(function1).apply("111"));
    }

結果如下:

在這裏插入圖片描述

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);
    }

簡單來說,Predicate 也是一種行爲的描述,對參入的對象作某些判斷,並返回一個boolean值,這些判斷邏輯即這個行爲的具體實現過程,則完全由使用者定義,其中還提供一些簡單的組合判斷,與、或、非以及 isEqual

@Test
    public void test4(){
        Predicate predicate = obj -> obj.equals("");
        Predicate predicate1 = obj -> Objects.isNull(obj);
        System.out.println(predicate.test(""));
        System.out.println(predicate.and(predicate1).test(""));
        System.out.println(predicate.or(predicate1).test(""));
        System.out.println(predicate.negate().test(""));
        System.out.println(predicate.and(predicate1).test(""));
    }

在這裏插入圖片描述

其中,and、or 的執行順序與java && 、|| 的執行順序一致。

其他接口

形如BiYYY類型接口

BiConsumer、BiFunction、BiPrediate 是**Consumer、Function、Predicate** 的擴展,可以傳入多個參數,沒有 BiSupplier 是因爲 Supplier 沒有入參。

形如XXXYYY類型接口

如果用 XXX 來表示 int、long、double、boolean ,用 YYY 來表示 Consumer、Function、Predicate、Supplier ,那麼JDK8就內置了此四種基本數據類型的消費,構建,操作,斷言以及他們因此而產生的一系列其他接口,在此不再贅述。

UnaryOperator與BinaryOperator

  • UnaryOperator:代表了一個作用於一個類型的操作,並且返回了同類型的結果
  • BinaryOperator:代表了一個作用於於兩個同類型的操作,並且返回了同類型的結果
  • XXXUnaryOperator:代表了一個作用於一個XXX數據類型的操作,並且返回了同類型數據的結果
  • XXXBinaryOperator:代表了一個作用於兩個XXX數據類型的操作,並且返回了同類型數據的結果

ToXXXFunction與ToXXXBiFunction類型接口

  • ToXXXFunction:接受一個輸入參數,返回一個XXX類型結果
  • ToDoubleBiFunction:接受兩個輸入參數,返回一個XXX類型結果

ObjXXXConsumer類型接口

對一個對象和一個基本數據類型的消費行爲

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