深入探尋JAVA8 part2:淺談幾個內置的函數式接口

前情提要

看此文前,不熟悉函數式編程和Lambda表達式的可以先看一下上文回憶一下。

本文將會簡單介紹Java8中內置的一些函數式接口

回顧函數式接口

函數式接口就是隻定義一個抽象方法的接口。在JAVA8以前,就有很多符合函數式接口定義的接口。

//比較器
public interface Comparator<T> {
    int compare(T o1, T o2);
}

//多線程接口
public interface Runnable{
    void run();
}

因爲JAVA8中還引入了默認方法的概念,所以即使接口中有多個默認方法,只要接口之定義了一個抽象方法,就滿足函數式接口的定義。

JAVA8中對這些可以定義爲函數式接口的接口加了一個@FuncationalInterface註解。如果一個接口中定義了多個抽象方法,又添加了這個註解,則會在編譯時拋出錯誤提示。

Consumer

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {
   
    void accept(T var1);

    default Consumer<T> andThen(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            this.accept(var2);
            var1.accept(var2);
        };
    }
}

這是JAVA8中對Consumer的定義,該函數式接口可以接收一個T類型的數據,並對該數據進行操作。JDK中一個典型的例子是forEach中對Consumer的使用,下面給出了ArrayList中的forEach源碼。

  @Override
  public void forEach(Consumer<? super E> consumer) {
    checkNotNull(consumer);
    for (E e : array) {
      consumer.accept(e);
    }
  }

forEach的接口定義中傳入了一個Consumer接口,並且調用Consumer的accept方法對數組中的每個元素進行處理。加入這是一個String數組,則可以使用如下的方式進行調用

list.forEach((String s) -> System.out::println);

在Consumer的接口定義中,還有一個andThen的默認方法,後面會再介紹一下這個默認方法。

因爲Consumer這個接口使用了泛型,因此只能使用基礎類型的封箱類型,如Integer,Long等。如果是對基礎類型的元素進行處理,可能會出現大量的封箱拆箱的操作,造成性能損耗。爲了解決這個問題,JAVA也提供了基礎類型的對應的Consumer接口,如IntConsumer:

@FunctionalInterface
public interface IntConsumer {

    void accept(int value);

    
    default IntConsumer andThen(IntConsumer after) {
        Objects.requireNonNull(after);
        return (int t) -> { accept(t); after.accept(t); };
    }
}

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

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

Predicate函數式接口定義了test抽象方法,它會對T對象執行判斷邏輯,並返回布爾類型的判斷接口。

還是以ArrayList中的一個使用場景爲例。ArrayList中提供了一個removeIf方法,該方法傳入了Predicate接口,並利用該接口判斷是否要刪除這個對象:

  public boolean removeIf(Predicate<? super E> filter) {
    checkNotNull(filter);

    E[] newArray = null;
    int newIndex = 0;
    for (int index = 0; index < array.length; ++index) {
      E e = array[index];

      //使用Predicate的test方法判斷是否要刪除該對象
      if (filter.test(e)) {
        if (newArray == null) {
          newArray = ArrayHelper.clone(array, 0, index);
          newIndex = index;
        }
      } else if (newArray != null) {
        newArray[newIndex++] = e;
      }
    }

    if (newArray == null) {
      return false;
    }
    array = newArray;
    return true;
  }

當然了,同Consumer一樣,它也提供了很多可以傳入基礎類型的Predicate接口,如IntPredicate,DoublePredicate等

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中定義了apply抽象方法,該抽象方法會接受一個T類型的對象,並轉化爲R類型的對象返回。使用方法和上面也沒啥區別,這裏就不具體贅述了。當然了,Function也爲基礎類型做了很多擴展,比如IntToDoubleFunction就可以將int轉化爲double型,還有ToDoubleFunction<T>則支持將T對象轉化爲double基礎型。

複合Lambda表達式

複合Lambda表達式是指將多個同類型的Lambda表達式按照一定語法進行組合,生成新的Lambda表達式。以比較基礎的Predicate作爲例子。Predicate中有以下幾個默認方法:and,negate,or,分別對應與,否定和或。

舉個例子,現在有兩個Predicate分別是判斷訂單的狀態是否爲已支付以及訂單的實付金額是否大於100。兩個Predicate如下:

Predicate<Order> p1 = (Order o) -> o.isPaid;
Predicate<Order> p2 = (Order o) -> o.actualFee > 100;

假如現在想要判斷是已支付且實付金額大於100的訂單,則新的Predicate可以通過上面兩個Predicate組合生成,利用and默認方法:

Predicate<Order> p3 = p1.and(p2);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章