Lambda 表達式

lambda
什麼是 Lambda 表達式,我以爲是咱們開始學習 Lambda 表達式以前應該要弄清楚的一個概念。咱們能夠把 Lambda 表達式理解爲簡潔地表示能夠傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個能夠拋出的異常列表。1
簡單的說 Lambda 有如下的特色:
1.匿名,它沒有一個明確的名字。
2.函數,是的, Lambda 表達式確實是一種函數,這毋庸置疑。
3.傳遞,Lambda 表達式能夠做爲參數傳遞給方法。
4.簡潔,沒有什麼比 Lambda 更加簡潔的語法了(Java 中)java

Lambda 管中窺豹

概念說完咱們就開始咱們的代碼之旅——Lambda 的語法:
(paramenters) -> expression
或者
(paramenters) -> { statements; }數據庫

Lambda 參數 + 箭頭 + Lambda 主體 構成。在 Java8 中 Lambda 表達式的體現形式有如下 5 中:
1.(String s) -> s.length()
這個 Lambda 表達式具備一個 String 類型的參數而且返回一個 Int。2express

2.(Apple a) -> a.getWeight() > 150
這個 Lambda 表達式具備一個 Apple 類型的參數而且返回一個 Boolean。多線程

3.(int x, int y) -> (System.out.println(x + y))
這個 Lambda 表達式具備一個 String 類型的參數然而它並無返回值(void 返回)。hexo

4.() -> 93
這個 Lambda 表達式沒有參數,返回一個 Int。app

5.(Apple a, Apple b) -> a1.getWeight().compareTo(a2.getWeight())
最後一個 Lambda 表達式具備兩個 Apple 類型的參數,返回一個 Int。函數

在哪裏以及如何使用 Lambda?

咱們剛剛已經瞭解了 Lambda 表達式的語法以及基本的 5 中表現形式,那麼應該在哪裏使用 Lambda 表達式以及如何使用 Lambda 表達式呢?通常咱們能夠在函數式接口3上使用 Lambda 表達式。附錄 A 列舉了 JDK 8以前已有的函數式接口。學習

咱們最經常使用的應該是 java.lang.Runnable 接口了,在 Java8 以前要開啓一個多線程,那麼一定會寫如下這段代碼:flex

Runnable run = new Runnable(){
    public void run(){
        System.out.println("Start a  new Thread");
    }
};

按照正常的縮進,咱們開啓一個線程最少須要寫 5 行代碼,其中只有一行使咱們的主要核心,其餘 4 行代碼都是爲之服務的,或者稱之爲樣板式代碼。有點像 JDBC 數據庫鏈接池同樣,先要建立鏈接對象...最後關閉鏈接池。而使用 Lambda 表達式能夠有效的縮短代碼的行數,而且讓代碼寫的更加的清晰。線程

Runnable run = () -> System.out.println("Start a  new Thread");

沒錯,就這麼簡單,一切顯得是那麼的優雅和華麗,沒有什麼多餘的樣板式代碼,一眼就可以看代碼的意圖。不再用主動過濾掉無用的代碼了,應爲留下來的都是有用的代碼。可能你一時還不習慣這樣的方式或是寫法,不要緊接着往下,會有更多的列子和驚喜。

剛剛咱們用函數式接口來使用 Lambda 表達式,還有一種方式是使用函數描述符——函數式接口的抽象方法基本上就是 Lambda 表達式的簽名。就拿剛剛線程的代碼來講,Runnable 接口能夠看作一個什麼都沒有返回的的函數簽名,應爲它只有一個 run 的抽象方法,這個方法既不接受參數也不返回參數。

public void process(Runnable r){
    r.run();
}

process(() -> System.out.println("Start a  new Thread"));

來一個栗子

廢話很少說,先上代碼:

public class ExecuteAround {

    public static void main(String ...args) throws IOException{

        // method we want to refactor to make more flexible
        String result = processFileLimited();
        System.out.println(result);

        System.out.println("---");
        
        // 使用 Lambda 表達式改進
        String oneLine = processFile((BufferedReader b) -> b.readLine());
        System.out.println(oneLine);

        String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());
        System.out.println(twoLines);

    }

    public static String processFileLimited() throws IOException {
        try (BufferedReader br =
                     new BufferedReader(new FileReader("data.txt"))) {
            return br.readLine();
        }
    }


    public static String processFile(BufferedReaderProcessor p) throws IOException {
        try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
            return p.process(br);
        }

    }

    public interface BufferedReaderProcessor{
        public String process(BufferedReader b) throws IOException;

    }
}

這個例子就是咱們平時處理資源的一段代碼,咱們打開一個資源,作一些處理(讀啊,寫啊),而後關閉資源。這裏對比了使用傳統方式和使用 Lambda 表達式的方式來寫這一段代碼。這裏能夠看出使用 Lambda 表達式可使代碼更加靈活,在這段代碼中的提現是:能夠由傳入參數的不一樣而決定方法的實現。

使用函數式接口

正如前面說的,函數式接口定義且只定義了一個抽象方法。咱們在 JDK8 以前經常使用的如 Comparable、Runnable 和 Callable 等,在 Java8 中偉大的設計師又引進了幾個新的函數式接口,這裏介紹其中的一些做爲了解。

Predicate

java.util.function.Predicate 接口定義了一個名叫 test 的抽象方法,它接受泛型 T 對象,並返回一個 boolean。

@FunctionalInterface
public interface Predicate<T>{
    boolean test(T t);
}

public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){
                result.add(apple);
            }
        }
        return result;
}   

List<Apple> greenApples = filter(inventory, (Apple a) -> "green".equals(a.getColor()));
System.out.println(greenApples);

Consumer

java.util.function.Consumer 接口定義了一個名叫 accept 的抽象方法,它接受泛型 T 對象,但並無返回值。若是須要訪問類型 T 的對象,並對其執行某些操做,就可使用這個接口。好比循環一個對象。

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

public static <T> forEach(List<T> list, Consumer<T> c){
    for(T i: list){
        c.accept(i);
    }
}

List<int> list = Arrays.asList(1, 2, 3, 4, 5);
//Lambda 是 Consumer 中 accept 方法的實現
forEach(list, (Integer i) -> System.out.println(i))

Function

java.util.function.Function<T, R> 接口定義了一個叫作 apply 的方法,該方法接受一個泛型的 T 對象,返回一個泛型的 R 對象。若是你須要一個 Lambda 表達式將對象的信息映射到輸出,那麼久可使用這個接口。

@FunctionalInterface
public interface Function<T, R>{
    R apply(T t);
}

public static <T, R> List<R> map(List<T> list, Function<T, R> f){
    List<R> result = new ArrayList<>();
    for(T s: list){
        result.add(f.apply(s));
    }
    return result;
}

List<int> list = Arrays.asList("java8", "in", "action");
List<Interger> listFun = map {
    list,(String s) -> s.length();
};

附錄A

JDK 8以前已有的函數式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener

本文由我的 hexo 博客 co2fe.com 遷移
date: 2017-07-16 22:03:37


  1. Java8 實戰中對 Lambda 的描述。

  2. 注意 Lambda 沒有 return 語句,其自己隱含了一個 return,故不用顯示聲明出來。

  3. 函數式接口(Functional Interface)是Java 8對一類特殊類型的接口的稱呼。 這類接口只定義了惟一的抽象方法的接口。

相關文章
相關標籤/搜索