Java8 - lambda 表達式

lambda表達式

爲何須要lambda 表達式java

函數對 Java 而言並不重要,在 Java 的世界裏,函數沒法獨立存在。Lambda 表達式爲 Java 添加了缺失的函數式編程特色,使咱們能將函數當作一等公民看待。儘管不徹底正確,咱們很快就會見識到 Lambda 與閉包的不一樣之處,可是又無限地接近閉包。在支持一類函數的語言中,Lambda 表達式的類型將是函數。可是,在 Java 中,Lambda 表達式是對象,他們必須依附於一類特別的對象類型——函數式接口(functional interface)。express

 

什麼是lambda 表達式編程

Java 中的 Lambda 表達式一般使用 (argument) -> (body) 語法書寫,例如:閉包

(arg1, arg2...) -> { body } (type1 arg1, type2 arg2...) -> { body }

如下是一些 Lambda 表達式的例子:ide

(int a, int b) -> { return a + b; }函數式編程

() -> System.out.println("Hello World");函數

(String s) -> { System.out.println(s); }ui

() -> 42this

() -> { return 3.1415 };spa

讓咱們瞭解一下 Lambda 表達式的結構。

  • 一個 Lambda 表達式能夠有零個或多個參數

  • 參數的類型既能夠明確聲明,也能夠根據上下文來推斷。例如:(int a)與(a)效果相同

  • 全部參數需包含在圓括號內,參數之間用逗號相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)

  • 空圓括號表明參數集爲空。例如:() -> 42

  • 當只有一個參數,且其類型可推導時,圓括號()可省略。例如:a -> return a*a

  • Lambda 表達式的主體可包含零條或多條語句

  • 若是 Lambda 表達式的主體只有一條語句,花括號{}可省略。匿名函數的返回類型與該主體表達式一致

  • 若是 Lambda 表達式的主體包含一條以上語句,則表達式必須包含在花括號{}中(造成代碼塊)。匿名函數的返回類型與代碼塊的返回類型一致,若沒有返回則爲空

lambda表達式舉例,

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class LambdaTest {


    /**
     * (arg1, arg2...) -> { body }
     * <p>
     * (type1 arg1, type2 arg2...) -> { body }
     *
     * @param args
     */
    public static void main(String[] args) {

        FunctionalWorkInterface.class.getName();

        /**
         * lambda 表達式
         */
        Arrays.asList("a", "b", "d").forEach(e -> {
            System.out.print(e);
        });

        Arrays.asList(1, 2, 8, 4, 5, 7).forEach(e -> System.out.println(e));

        Arrays.asList("a", "b", "d").sort((e1, e2) -> e1.compareTo(e2));


        List<Integer> arr = Arrays.asList(1, 2, 8, 4, 5, 7);

        /**
         * 定義一個lambda 表達式
         * 集中不一樣形式的 lambda 表達式定義方式
         */
        Comparator<Integer> comparator = (Integer a, Integer b) -> {
            return a.compareTo(b);
        };


        arr.sort(comparator);

        arr.sort((a, b) -> {
            return a - b;
        });

        // 不須要return
        arr.sort((a, b) -> a - b);

        arr.forEach(e -> System.out.println(e));


        //舊方法:
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello from thread");
            }
        }).start();

        //新方法:
        new Thread(
                () -> System.out.println("Hello from thread")
        ).start();

    }
}

 

函數式接口(Functional interface

@FunctionalInterface是 Java 8 新加入的一種接口,用於指明該接口類型聲明是根據 Java 語言規範定義的函數式接口。Java 8 還聲明瞭一些 Lambda 表達式可使用的函數式接口,當你註釋的接口不是有效的函數式接口時,會產生@FunctionalInterface 編譯層面的錯誤。

好比 下面這個接口就是函數式接口java.util.Comparator ,

@FunctionalInterface
public interface Comparator<T> {
  
    int compare(T o1, T o2);
 
    boolean equals(Object obj);

    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }

    default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }
}

根據定義,函數式接口只能有一個抽象方法,若是你嘗試添加第二個抽象方法,將拋出編譯時錯誤。例如:

image.png

Error:(3, 1) java: 意外的 @FunctionalInterface 註釋

com.common.FunctionalWorkInterface 不是函數接口

在 接口 com.common.FunctionalWorkInterface 中找到多個非覆蓋抽象方法

存在多個抽象方法就會報錯。

 

下面是 函數式接口 註解的用法

package com.common;

/**
 * 使用函數式接口 註解標註一個接口 ,該接口只能有一個抽象方法。
 */
@FunctionalInterface
interface FunctionalWorkInterface {

    void doSomeWork();

}

public class TestMain {

    public static void execute(FunctionalWorkInterface worker) {
        worker.doSomeWork();
    }

    public static void main(String[] args) {

        //invoke doSomeWork using Annonymous class
        execute(new FunctionalWorkInterface() {
            @Override
            public void doSomeWork() {
                System.out.println("do some work...");
            }
        });

        //invoke doSomeWork using Lambda expression
        execute(() -> System.out.println("do some work from lambda..."));
    }

}

這上面的例子裏,建立了自定義的函數式接口並與 Lambda 表達式一塊兒使用。

execute() 方法如今能夠將 Lambda 表達式做爲參數。

 

Lambda 表達式與匿名類的區別

使用匿名類與 Lambda 表達式的一大區別在於關鍵詞的使用。對於匿名類,關鍵詞 this 解讀爲匿名類,而對於 Lambda 表達式,關鍵詞 this 解讀爲寫就 Lambda 的外部類。

Lambda 表達式與匿名類的另外一不一樣在於二者的編譯方法。Java 編譯器編譯 Lambda 表達式並將他們轉化爲類裏面的私有函數,它使用 Java 7 中新加的 invokedynamic 指令動態綁定該方法。

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class LambdaThisTest {

    public void sort(List<Integer> list) {

        list.sort((a, b) -> {
            System.out.print(this.getClass().getName());
            return a.compareTo(b);
        });
    }

    public void sort02(List<Integer> list) {

        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(this.getClass().getName());
                return o1.compareTo(o2);
            }
        });
    }

    public static void main(String[] args) {
        LambdaThisTest lambdaThisTest = new LambdaThisTest();
        lambdaThisTest.sort(Arrays.asList(1, 5, 6));
        lambdaThisTest.sort02(Arrays.asList(1, 5, 6));
    }
}

輸出,

com.common.LambdaThisTestcom.common.LambdaThisTestcom.common.LambdaThisTest$1

com.common.LambdaThisTest$1

===========END===========

相關文章
相關標籤/搜索