1. 程式人生 > >Android tips(十三)-->Android開發過程中使用Lambda表示式

Android tips(十三)-->Android開發過程中使用Lambda表示式

新的Java8 API中提供了不少新的特性,其中就有Lambda表示式。而本文我們將主要介紹一下在Android開發過程中如何使用Lambda表示式,這裡主要是為我們後續介紹RxAndroid、RxJava相關知識做鋪墊的。

  • Lambda表示式的概念

Lambda表示式是Java8中提供的一種新的特性,它支援Java也能進行簡單的“函數語言程式設計”,即Lambda允許你通過表示式來代替功能介面。其實Lambda表示式的本質只是一個”語法糖”,由編譯器推斷並幫你轉換包裝為常規的程式碼,因此你可以使用更少的程式碼來實現同樣的功能。

Lambda表示式就和方法一樣,它提供了一個正常的引數列表和一個使用這些引數的主體(body,可以是一個表示式或一個程式碼塊)

咋樣很厲害吧?下面我們將慢慢看一下Lambda表示式的相關知識。

  • 標準的Lambda表示式寫法

那麼Lambda表示式具體如何編寫呢?下面我們可以看一個具體的Lambda表示式例項。

(int x, int y) -> {
    Log.i("TAG", "x:" + x + "  y:" + y);
    return x + y;
}

這是一個標準的Lambda表示式的寫法,一個Lambda表示式通常有三部分組成:

  • 引數:(int a, int b)是這個lambda expression的引數部分,包括引數型別和引數名

  • 箭頭:->

  • 程式碼塊:就是用”{}”包含著的那兩句程式碼。

其中由於引數的型別是可以通過系統上下文推斷出來的,所以在很多情況下,引數的型別是可以省略的,可以省略的寫成:

(x, y) -> {
    Log.i("TAG", "x:" + x + "  y:" + y);
    return x + y;
}

其實不光引數型別是可以省略的,程式碼塊也是可以省略的,比如如果我們的程式碼塊中只有一句程式碼,我們可以寫成:

(x, y) -> 
    return x + y;

也可以寫成:

(x, y) -> return x + y

而這個時候其實return關鍵字也是可以省略的,這時候我們就可以這樣寫:

(x, y) -> x + y

好精簡有木有…

  • Lambda表示式的幾個特性

(1)型別推導
編譯器負責推導Lambda表示式的型別。它利用Lambda表示式所在上下文所期待的型別進行推導, 這個被期待的型別被稱為目標型別。就是說我們傳入的引數可以無需寫型別了!

(2)變數捕獲
對於lambda表示式和內部類, 我們允許在其中捕獲那些符合有效只讀(Effectively final)的區域性變數。簡單的說,如果一個區域性變數在初始化後從未被修改過,那麼它就符合有效只讀的要求, 換句話說,加上final後也不會導致編譯錯誤的區域性變數就是有效只讀變數

(3)方法引用
如果我們想要呼叫的方法擁有一個名字,我們就可以通過它的名字直接呼叫它:

Comparator byName = Comparator.comparing(Person::getName); 

此處無需再傳入引數,Lambda會自動裝配成Person型別進來然後執行getName()方法,而後返回getName()的String

方法引用有很多種,它們的語法如下:

靜態方法引用:ClassName::methodName
例項上的例項方法引用:instanceReference::methodName
超類上的例項方法引用:super::methodName
型別上的例項方法引用:ClassName::methodName
構造方法引用:Class::new
陣列構造方法引用:TypeName[]::new

  • 學習使用Lambda表示式
/**
 * 定義執行緒測試例子
 */
public static void m1() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i("TAG", "定義一個簡單測試例子...");
            }
        }).start();
    }

以上是我們使用的普通的Java程式碼實現的一個簡單的執行緒例子,那麼如果使用Lambda表示式是什麼形式的呢?

/**
 * 使用Lambda表示式實現
 */
public static void m2() {
        new Thread(
                () -> {
                    Log.i("TAG", "使用Lambda表示式的例子...");
                }
        ).start();
    }

可以看到通過Lambda表示式程式碼還是相當簡潔的

(1)我們直接在編輯器裡面寫Lambda表示式是會報錯的,因為Lambda不可以這樣使用。在關於Lambda的使用其實需要跟一個叫做函式介面(Functional Interface)的東西繫結在一起。什麼是函式介面呢?函式介面是在Java8 中引入的概念,其就是為了Lambda表示式而引入的。我們知道Java中的介面的概念,而函式介面其實就是:

一個只定義了一個抽象方法的介面

比如ClickListener這個介面就只有一個onClick方法,那麼它就是一個函式介面。在Android開發過程中我們經常會這樣使用OnClickListener:

/**
 * 定義OnClickListener,處理按鈕點選事件
 */
View.OnClickListener onClickListener = newView.OnClickListener() {    
    @Override
    public void onClick(View view) {
        // 處理按鈕點選事件
        doSomeThing();
    }
});
findViewById(R.id.titleView).setOnClickListener(onClickListener);

其實我們除了上面定義的OnClickListener我們也可以直接使用匿名內部類:

/**
 * 定義匿名內部類處理元件的點選事件
 */
findViewById(R.id.titleView).setOnClickListener(new View.OnClickListener() {    
    @Override
    public void onClick(View view) {
        // 處理元件的點選事件
        doSomeThing();
    }
});

在上面的程式碼中,我們可以發現其實我們主要是呼叫其他的doSomeThing()方法,該方法中實現了我們的按鈕點選事件,並且這裡我們通過一系列的縮排,括號來實現了這個呼叫操作,有木有覺得很繁瑣?在Java 8出現之前,在程式碼中經常有這樣的實現。現在好了,有了Lambda達表示,我們可以這樣寫了:

/**
 * 自定義OnClickListener按鈕點選事件
 */
View.OnClickListener onClickListener = view -> doSomeThing();
findViewById(R.id.titleView).setOnClickListener(onClickListener);

至於匿名內部類我們也可以這樣寫:

findViewById(R.id.titleView).setOnClickListener(view -> doSomeThing());

通過使用Lambda表示式之後程式碼就變得相當簡潔了。從上面的例子可以看到,Lambda表示式簡化了函式介面的例項的建立,可以在程式碼層次簡化函式介面,內部匿名類的寫法等。

  • Lambda表示式的優缺點

上面我們簡單的介紹了Lambda表示式的概念,寫法與特性等,那麼它具體有什麼優缺點呢?

優點:

  • 使用Lambda表示式可以極大的簡化程式碼。去除了很多無用的Java程式碼,使得程式碼更為簡潔明瞭;

缺點:

  • 可讀性差。在程式碼簡潔的情況下,另一方面又讓大多程式設計師很難讀懂。因為很少程式設計師接觸使用它。

  • 如何在Android Studio中使用Lambda表示式

Android Studio預設使用Lambda表示式是會報錯的,即使你使用的是Java 8,升級Android Studio的Language level為1.8

這裡寫圖片描述

如果是非Java8,那麼我們如何使用Lambda表示式呢?

幸好有Lambda的gradle外掛gradle-retrolambda,通過這個外掛我們可以在Android Studio中使用Lambda表示式了,其可以相容到Java5。

具體如:

/**
 * 自定義元件點選事件
 */
imageView.setOnClickListener { view ->
            Log.i("TAG", "按鈕點選事件...")
        }

總結:

相對來說使用Lambda表示式還是能夠優化一些程式碼的,但是相應的程式碼的可能性會有相應的下降,在實際的開發過程中可根據具體的情況作出相應的選擇。