Lambda表達(dá)式與函數(shù)式接口

前言

Java8,自2014年3月18日發(fā)布至今,出來已經(jīng)有5年時(shí)間了,Java12都已經(jīng)發(fā)布,但其實(shí)在Android的開發(fā)中,大部分人,即使在項(xiàng)目中已經(jīng)配置了Java8的使用,但是還有人一直使用的是Java6和Java7的屬性和方法,這使得有時(shí)候在開發(fā)過程中難免會(huì)出現(xiàn)一些問題。

舉個(gè)例子:
大多數(shù)人使用時(shí)間工具的時(shí)候,還是使用java.util.Date這個(gè)類,但其實(shí)這個(gè)類已經(jīng)是被棄用的了,在JDK1.8中提供了java.time.LocalDate、java.time.LocalDateTime兩個(gè)類給開發(fā)者使用,來看下面的例子。

Date date = new Date();
System.out.println(date);
date.setYear(date.getYear() + 1)
System.out.println(date);

LocalDate date1 = new LocalDate().now();
System.out.println(date1);
LocalDate date2 = date1.plusDays(1);
System.out.println(date1);
System.out.println(date2);

輸出:
Sun Oct 18 16:37:53 CST 2019
Sun Oct 18 16:37:53 CST 2020
2019-10-18
2019-10-18
2019-10-19

我們可以看到date對象是可以被改變的,而date1對象是不能被改變的,只有通過調(diào)用plusDays方法后返回的date2對象才是改變的,這其實(shí)是新建了一個(gè)LocalDate對象,這就是不可變性。

這里解釋一下不可變性,其實(shí)就是對象在實(shí)例化之后不可修改
好處:

  • 對象的安全問題,防止對象的值被篡改。
  • 不可變對象是多線程安全的。
  • 不可變對象可以實(shí)現(xiàn)對象池,實(shí)現(xiàn)緩存機(jī)制。

不可變對象例子:String、BigDecimal、LocalDate、LocalDateTime。

Lambda表達(dá)式

Lambda表達(dá)式是JDK1.8中最重要的新特性之一。

使用Lambda表達(dá)式可以代替只有一個(gè)抽象方法的接口實(shí)現(xiàn),告別匿名內(nèi)部類,代碼看起來更簡潔易懂。

舉個(gè)例子:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("runnable opt");
    }
}).start();

可以寫成

new Thread(() -> System.out.println("runnable opt")).start();

六行代碼變成一行代碼了,是不是變簡潔了呢。

Lambda表達(dá)式同時(shí)還提升了對集合、框架的迭代、遍歷、過濾數(shù)據(jù)的操作。

Lambda表達(dá)式特點(diǎn)

  • 函數(shù)式編程。
  • 參數(shù)類型自動(dòng)推斷。
  • 代碼量少,簡潔,更容易實(shí)現(xiàn)并行。

如何學(xué)好Lambda表達(dá)式

  • 熟悉泛型。
  • 多練,多用Stream API。

Lambda表達(dá)式的使用場景

  • 任何有函數(shù)式接口的地方。

什么是函數(shù)式接口

  • 只有一個(gè)抽象方法的接口(Object類里面的方法除外),就叫函數(shù)式接口。

舉個(gè)例子:

public interface UserMapper {
    int delete();
    public int hashCode();
    default int insert() {
        return 1;
    }
    static int update() {
        return 1;
    }
}

一定有人奇怪,為什么這個(gè)接口明明有兩個(gè)抽象方法啊,怎么能算函數(shù)式接口,我可肯定的告訴你,是的,這是函數(shù)式接口,你可以使用@FunctionalInterface注解去判斷接口是不是函數(shù)式接口。

答案就在hashCode方法,因?yàn)檫@個(gè)方法是Object類里面的方法,所以就算ta是抽象方法,也是一個(gè)不滿足條件的抽象方法,而且函數(shù)式接口可以包含默認(rèn)方法和靜態(tài)方法。

其實(shí)不使用@FunctionalInterface注解標(biāo)識也可以,這個(gè)注解只是給編譯器去識別,進(jìn)行檢查是否存在一個(gè)抽象方法。

JDK1.8之前的一些函數(shù)式接口

  • java.lang.Runnable
  • java.util.concurrent.Callable<V>
  • java.util.Comparator<V>

我們可以看到,其實(shí)函數(shù)式接口會(huì)大量應(yīng)用到泛型,這也是學(xué)Lambda表達(dá)式之前,要先學(xué)好泛型的原因。

除了這些,JDK1.8也給我們提供了一些好用的函數(shù)式接口。

接口名 參數(shù) 返回值 用途 含義
Predicate T boolean 斷言 代表一個(gè)輸入
Consumer T void 消費(fèi) 代表一個(gè)輸入
Function<T,R> T R 函數(shù) 代表一個(gè)輸入,一個(gè)輸出(一般輸入和輸出是不同類型的)
BiFunction<T,U,R> (T,U) R 函數(shù) 代表兩個(gè)輸入,一個(gè)輸出(一般輸入和輸出是不同類型的)
Supplier None T 工廠方法 代表一個(gè)輸出
UnaryOperator T T 邏輯非 代表一個(gè)輸入,一個(gè)輸出(輸入和輸出是相同類型的)
BinaryOperator (T,T) T 二元操作 代表兩個(gè)輸入,一個(gè)輸出(輸入和輸出是相同類型的)

Lambda表達(dá)式語法

Lambda表達(dá)式是對象,是一個(gè)函數(shù)式接口的實(shí)例。

Lambda表達(dá)式圖解.png

那為什么可以改成這樣呢,我們先看一下,lambda的格式
參數(shù)名+操作,(argument) -> (operation)
Runnable的抽象方法void run()沒有參數(shù),沒有返回值,所以最后的寫法就如上面那樣。

  • () 里面的參數(shù)的個(gè)數(shù),根據(jù)函數(shù)式接口里面的抽象方法的參數(shù)個(gè)數(shù)來決定。
  • 當(dāng)只有一個(gè)參數(shù)的時(shí)候,()可以省略,無或者有多個(gè)參數(shù)時(shí)不能省略。
  • 當(dāng)operation邏輯非常簡單的時(shí)候,{}和return可以省略。
  • argument的參數(shù)類型可以省略,由編譯器自動(dòng)推斷。

Lambda表達(dá)式示例

示例 含義
() -> {} 無參,無返回值
() -> System.out.println(1) 無參,無返回值,省略{}
() -> 100 無參,有返回值,省略return和{}
(int x) -> x + 1 一個(gè)參數(shù),有返回值,省略return和{}
x -> x + 1 一個(gè)參數(shù),省略()和參數(shù)類型,有返回值,省略return和{}
(int x, int y) -> x + y 兩個(gè)參數(shù),有返回值,省略return和{}
(x, y) -> {} 兩個(gè)參數(shù),省略參數(shù)類型,有返回值,省略return和{}

注意事項(xiàng)

  • 多個(gè)參數(shù)時(shí)省略參數(shù)類型,不能部分省略。
    (x, int y) -> x + y 這種部分省略的方式是錯(cuò)誤的寫法。
  • 參數(shù)類型不能使用final修飾符。
    (x, final y) -> x + y 這種寫法是錯(cuò)誤的。
  • Lambda表達(dá)式不能直接賦值給Object對象。
    Object object = (Supplier<?>) () -> "hello" 如果需要賦值給Object對象,需要把Lambda表達(dá)式強(qiáng)轉(zhuǎn)成函數(shù)式接口。
  • Lambda表達(dá)式不需要也不允許使用throws語句來聲明它可能會(huì)拋出的異常。

Lambda表達(dá)式的官方文檔

環(huán)境配置

因?yàn)镴DK的限制,我們不能使用lambda表達(dá)式,但我們又希望學(xué)習(xí)lambda表達(dá)式,其實(shí)最新版本的AndroidStudio已經(jīng)可以使用兼容的lambda表達(dá)式,只要我們配置一下環(huán)境即可。

修改build.gradle文件

defaultConfig {
  ...
    jackOptions {
      enabled=true
    }
  }
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
  ...
}

然后在項(xiàng)目中就可以愉快使用lambda表達(dá)式了??!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容