首頁>技術>

一. Lambda 表示式

Lambda表示式 (也稱為閉包) 是整個 Java 8 發行版中最受期待的在 Java 語言層面上的改變,Lambda 允許把函式作為一個方法的引數,即 行為引數化,函式作為引數傳遞進方法中。

什麼是 Lambda 表示式

我們知道,對於一個 Java 變數,我們可以賦給一個 「值」

如果你想把 「一塊程式碼」 賦給一個 Java 變數,應該怎麼做呢?

比如,我想把右邊的程式碼塊,賦值給一個叫做 blockOfCode 的 Java 變數:

在 Java 8 之前,這個是做不到的,但是 Java 8 問世之後,利用 Lambda 特性,就可以做到了。

當然,這個並不是一個很簡潔的寫法,所以為了讓這個賦值操作變得更加優雅,我們可以移除一些沒有必要的宣告。

這樣,我們就成功的非常優雅的把「一塊程式碼」賦給了一個變數。而「這塊程式碼」,或者說「這個被賦給一個變數的函式」,就是一個 Lambda 表示式

但是這裡仍然有一個問題,就是變數 blockOfCode 的型別應該是什麼?

在 Java 8 裡面,**所有的 Lambda 的型別都是一個介面,而 Lambda 表示式本身,也就是「那段程式碼」,需要是這個介面的實現。**這是理解 Lambda 的一個關鍵所在,簡而言之就是,Lambda 表示式本身就是一個介面的實現。直接這樣說可能還是有點讓人困擾,我們繼續看看例子。我們給上面的 blockOfCode 加上一個型別:

這種只有一個介面函式需要被實現的介面型別,我們叫它「函式式介面」。

為了避免後來的人在這個介面中增加介面函式導致其有多個介面函式需要被實現,變成「非函式介面」,我們可以在這個上面加上一個宣告 @FunctionalInterface, 這樣別人就無法在裡面新增新的介面函數了:

這樣,我們就得到了一個完整的 Lambda 表示式宣告:

Lambda 表示式的作用

Lambda 最直觀的作用就是使程式碼變得整潔.。

我們可以對比一下 Lambda 表示式和傳統的 Java 對同一個介面的實現:

這兩種寫法本質上是等價的。但是顯然,Java 8 中的寫法更加優雅簡潔。並且,由於 Lambda 可以直接賦值給一個變數,我們就可以直接把 Lambda 作為引數傳給函式, 而傳統的 Java 必須有明確的介面實現的定義,初始化才行。

有些情況下,這個介面實現只需要用到一次。傳統的 Java 7 必須要求你定義一個“汙染環境”的介面實現 MyInterfaceImpl,而相較之下 Java 8 的 Lambda, 就顯得乾淨很多。

二. 函式式介面

上面我們說到,只有一個介面函式需要被實現的介面型別,我們叫它「函式式介面」。Lambda 表示式配合函式式介面能讓我們程式碼變得乾淨許多。

Java 8 API 包含了很多內建的函式式介面,在老 Java 中常用到的比如Comparator或者Runnable介面,這些介面都增加了@FunctionalInterface註解以便能用在Lambda上。

Java 8 API 同樣還提供了很多全新的函式式介面來讓工作更加方便,有一些介面是來自 Google Guava 庫裡的,即便你對這些很熟悉了,還是有必要看看這些是如何擴充套件到 Lambda 上使用的。

1 - Comparator(比較器介面)

Comparator是老Java中的經典介面, Java 8 在此之上添加了多種預設方法。原始碼及使用示例如下:

@FunctionalInterfacepublic interface Comparator<T> {    int compare(T o1, T o2);}
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);Person p1 = new Person("John", "Doe");Person p2 = new Person("Alice", "Wonderland");comparator.compare(p1, p2);             // > 0comparator.reversed().compare(p1, p2);  // < 0

2 - Consumer(消費型介面)

Consumer 介面表示執行在單個引數上的操作。原始碼及使用示例如下:

@FunctionalInterfacepublic interface Consumer<T> {    void accept(T t);}
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);greeter.accept(new Person("Luke", "Skywalker"));

更多的Consumer介面

BiConsumer:void accept(T t, U u);: 接受兩個引數的二元函式

DoubleConsumer:void accept(double value);: 接受一個double引數的一元函式

IntConsumer:void accept(int value);: 接受一個int引數的一元函式

LongConsumer:void accept(long value);: 接受一個long引數的一元函式

ObjDoubleConsumer:void accept(T t, double value);: 接受一個泛型引數一個double引數的二元函式

ObjIntConsumer:void accept(T t, int value);: 接受一個泛型引數一個int引數的二元函式

ObjLongConsumer:void accept(T t, long value);: 接受一個泛型引數一個long引數的二元函式

3 - Supplier(供應型介面)

Supplier 介面是不需要引數並返回一個任意範型的值。其簡潔的宣告,會讓人以為不是函式。這個抽象方法的宣告,同 Consumer相反,是一個只聲明瞭返回值,不需要引數的函式。也就是說 Supplier 其實表達的不是從一個引數空間到結果空間的對映能力,而是表達一種生成能力,因為我們常見的場景中不止是要consume(Consumer)或者是簡單的map(Function),還包括了 new 這個動作。而 Supplier 就表達了這種能力。原始碼及使用示例如下:

@FunctionalInterfacepublic interface Supplier<T> {    T get();}
Supplier<Person> personSupplier = Person::new;personSupplier.get();   // new Person

更多Supplier介面

BooleanSupplier:boolean getAsBoolean();: 返回boolean的無參函式

DoubleSupplier:double getAsDouble();: 返回double的無參函式

IntSupplier:int getAsInt();: 返回int的無參函式

LongSupplier:long getAsLong();: 返回long的無參函式

4 - Predicate(斷言型介面)

Predicate 介面只有一個引數,返回 boolean 型別。該介面包含多種預設方法來將 Predicate 組合成其他複雜的邏輯(比如:)。Stream 的 filter 方法就是接受 Predicate 作為入參的。這個具體在後面使用 Stream 的時候再分析深入。原始碼及使用示例如下:

@FunctionalInterfacepublic interface Predicate<T> {    boolean test(T t);}
Predicate<String> predicate = (s) -> s.length() > 0;predicate.test("foo");            // truepredicate.negate().test("foo");     // falsePredicate<Boolean> nonNull = Objects::nonNull;Predicate<Boolean> isNull = Objects::isNull;Predicate<String> isEmpty = String::isEmpty;Predicate<String> isNotEmpty = isEmpty.negate();

更多的Predicate介面

BiPredicate:boolean test(T t, U u);: 接受兩個引數的二元斷言函式

DoublePredicate:boolean test(double value);: 入參為double的斷言函式

IntPredicate:boolean test(int value);: 入參為int的斷言函式

LongPredicate:boolean test(long value);: 入參為long的斷言函式

5 - Function(功能型介面)

Function 介面有一個引數並且返回一個結果,並附帶了一些可以和其他函式組合的預設方法(compose, andThen)。原始碼及使用示例如下:

@FunctionalInterfacepublic interface Function<T, R> {    R apply(T t);}
Function<String, Integer> toInteger = Integer::valueOf;Function<String, String> backToString = toInteger.andThen(String::valueOf);backToString.apply("123");     // "123"

更多的Function介面

BiFunction :R apply(T t, U u);: 接受兩個引數,返回一個值,代表一個二元函式;

DoubleFunction :R apply(double value);: 只處理double型別的一元函式;

IntFunction :R apply(int value);: 只處理int引數的一元函式;

LongFunction :R apply(long value);: 只處理long引數的一元函式;

ToDoubleFunction:double applyAsDouble(T value);: 返回double的一元函式;

ToDoubleBiFunction:double applyAsDouble(T t, U u);: 返回double的二元函式;

ToIntFunction:int applyAsInt(T value);: 返回int的一元函式;

ToIntBiFunction:int applyAsInt(T t, U u);: 返回int的二元函式;

ToLongFunction:long applyAsLong(T value);: 返回long的一元函式;

ToLongBiFunction:long applyAsLong(T t, U u);: 返回long的二元函式;

DoubleToIntFunction:int applyAsInt(double value);: 接受double返回int的一元函式;

DoubleToLongFunction:long applyAsLong(double value);: 接受double返回long的一元函式;

IntToDoubleFunction:double applyAsDouble(int value);: 接受int返回double的一元函式;

IntToLongFunction:long applyAsLong(int value);: 接受int返回long的一元函式;

LongToDoubleFunction:double applyAsDouble(long value);: 接受long返回double的一元函式;

LongToIntFunction:int applyAsInt(long value);: 接受long返回int的一元函式;

6 - Operator

Operator 其實就是 Function,函式有時候也叫作運算元。運算元在Java8中介面描述更像是函式的補充,和上面的很多型別對映型函式類似。運算元 Operator 包括:UnaryOperator 和 BinaryOperator。分別對應單(一)元運算元和二元運算元。

運算元的介面宣告如下:

@FunctionalInterfacepublic interface UnaryOperator<T> extends Function<T, T> {    static <T> UnaryOperator<T> identity() {        return t -> t;    }}
@FunctionalInterfacepublic interface BinaryOperator<T> extends BiFunction<T,T,T> {    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {        Objects.requireNonNull(comparator);        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;    }    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {        Objects.requireNonNull(comparator);        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;    }}

Operator只需宣告一個泛型引數 T 即可。對應的使用示例如下:

UnaryOperator<Integer> increment = x -> x + 1;System.out.println("遞增:" + increment.apply(2)); // 輸出 遞增:3BinaryOperator<Integer> add = (x, y) -> x + y;System.out.println("相加:" + add.apply(2, 3)); // 輸出 相加:5BinaryOperator<Integer> min = BinaryOperator.minBy((o1, o2) -> o1 - o2);System.out.println("最小值:" + min.apply(2, 3)); // 輸出 最小值:2

更多的Operator介面

LongUnaryOperator:long applyAsLong(long operand);: 對long型別做操作的一元運算元

IntUnaryOperator:int applyAsInt(int operand);: 對int型別做操作的一元運算元

DoubleUnaryOperator:double applyAsDouble(double operand);: 對double型別做操作的一元運算元

DoubleBinaryOperator:double applyAsDouble(double left, double right);: 對double型別做操作的二元運算元

IntBinaryOperator:int applyAsInt(int left, int right);: 對int型別做操作的二元運算元

LongBinaryOperator:long applyAsLong(long left, long right);: 對long型別做操作的二元運算元

7 - 其他函式式介面

java.lang.Runnable

java.util.concurrent.Callable

java.security.PrivilegedAction

java.io.FileFilter

java.nio.file.PathMatcher

java.lang.reflect.InvocationHandler

java.beans.PropertyChangeListener

java.awt.event.ActionListener

javax.swing.event.ChangeListener

16
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Weblogic:記憶體溢位和記憶體洩漏問題的故障排查