成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Java 8 Lambda 表達(dá)式詳解

haoguo / 3360人閱讀

摘要:表達(dá)式簡(jiǎn)介表達(dá)式是一個(gè)匿名函數(shù)對(duì)于而言并不很準(zhǔn)確,但這里我們不糾結(jié)這個(gè)問(wèn)題。如果表達(dá)式的正文有一條以上的語(yǔ)句必須包含在大括號(hào)代碼塊中,且表達(dá)式的返回值類(lèi)型要與匿名函數(shù)的返回類(lèi)型相同。

版權(quán)聲明:本文由吳仙杰創(chuàng)作整理,轉(zhuǎn)載請(qǐng)注明出處:https://segmentfault.com/a/1190000009186509

1. 引言

在 Java 8 以前,若我們想要把某些功能傳遞給某些方法,總要去寫(xiě)匿名類(lèi)。以前注冊(cè)事件監(jiān)聽(tīng)器的寫(xiě)法與下面的示例代碼就很像:

manager.addScheduleListener(new ScheduleListener() {
    @Override
    public void onSchedule(ScheduleEvent e) {        
        // Event listener implementation goes here...
    }
});

這里我們添加了一些自定義代碼到 Schedule 監(jiān)聽(tīng)器中,需要先定義匿名內(nèi)部類(lèi),然后傳遞一些功能到 onSchedule 方法中。

正是 Java 在作為參數(shù)傳遞普通方法或功能的限制,Java 8 增加了一個(gè)全新語(yǔ)言級(jí)別的功能,稱(chēng)為 Lambda 表達(dá)式。

2. 為什么 Java 需要 Lambda 表達(dá)式

Java 是面向?qū)ο?/strong>語(yǔ)言,除了原始數(shù)據(jù)類(lèi)型之處,Java 中的所有內(nèi)容都是一個(gè)對(duì)象。而在函數(shù)式語(yǔ)言中,我們只需要給函數(shù)分配變量,并將這個(gè)函數(shù)作為參數(shù)傳遞給其它函數(shù)就可實(shí)現(xiàn)特定的功能。JavaScript 就是功能編程語(yǔ)言的典范(閉包)。

Lambda 表達(dá)式的加入,使得 Java 擁有了函數(shù)式編程的能力。在其它語(yǔ)言中,Lambda 表達(dá)式的類(lèi)型是一個(gè)函數(shù);但在 Java 中,Lambda 表達(dá)式被表示為對(duì)象,因此它們必須綁定到被稱(chēng)為功能接口的特定對(duì)象類(lèi)型。

3. Lambda 表達(dá)式簡(jiǎn)介

Lambda 表達(dá)式是一個(gè)匿名函數(shù)(對(duì)于 Java 而言并不很準(zhǔn)確,但這里我們不糾結(jié)這個(gè)問(wèn)題)。簡(jiǎn)單來(lái)說(shuō),這是一種沒(méi)有聲明的方法,即沒(méi)有訪(fǎng)問(wèn)修飾符,返回值聲明和名稱(chēng)。

在僅使用一次方法的地方特別有用,方法定義很短。它為我們節(jié)省了,如包含類(lèi)聲明和編寫(xiě)多帶帶方法的工作。

Java 中的 Lambda 表達(dá)式通常使用語(yǔ)法是 (argument) -> (body),比如:

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

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

以下是 Lambda 表達(dá)式的一些示例:

(int a, int b) -> {  return a + b; }

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

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

() -> 42

() -> { return 3.1415 };
3.1 Lambda 表達(dá)式的結(jié)構(gòu)

Lambda 表達(dá)式的結(jié)構(gòu):

Lambda 表達(dá)式可以具有零個(gè),一個(gè)或多個(gè)參數(shù)。

可以顯式聲明參數(shù)的類(lèi)型,也可以由編譯器自動(dòng)從上下文推斷參數(shù)的類(lèi)型。例如 (int a) 與剛才相同 (a)

參數(shù)用小括號(hào)括起來(lái),用逗號(hào)分隔。例如 (a, b)(int a, int b)(String a, int b, float c)。

空括號(hào)用于表示一組空的參數(shù)。例如 () -> 42

當(dāng)有且僅有一個(gè)參數(shù)時(shí),如果不顯式指明類(lèi)型,則不必使用小括號(hào)。例如 a -> return a*a。

Lambda 表達(dá)式的正文可以包含零條,一條或多條語(yǔ)句。

如果 Lambda 表達(dá)式的正文只有一條語(yǔ)句,則大括號(hào)可不用寫(xiě),且表達(dá)式的返回值類(lèi)型要與匿名函數(shù)的返回類(lèi)型相同。

如果 Lambda 表達(dá)式的正文有一條以上的語(yǔ)句必須包含在大括號(hào)(代碼塊)中,且表達(dá)式的返回值類(lèi)型要與匿名函數(shù)的返回類(lèi)型相同。

4. 方法引用 4.1 從 Lambda 表達(dá)式到雙冒號(hào)操作符

使用 Lambda 表達(dá)式,我們已經(jīng)看到代碼可以變得非常簡(jiǎn)潔。

例如,要?jiǎng)?chuàng)建一個(gè)比較器,以下語(yǔ)法就足夠了

Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

然后,使用類(lèi)型推斷:

Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

但是,我們可以使上面的代碼更具表現(xiàn)力和可讀性嗎?我們來(lái)看一下:

Comparator c = Comparator.comparing(Person::getAge);

使用 :: 運(yùn)算符作為 Lambda 調(diào)用特定方法的縮寫(xiě),并且擁有更好的可讀性。

4.2 使用方式

雙冒號(hào)(::)操作符是 Java 中的方法引用。 當(dāng)們使用一個(gè)方法的引用時(shí),目標(biāo)引用放在 :: 之前,目標(biāo)引用提供的方法名稱(chēng)放在 :: 之后,即 目標(biāo)引用::方法。比如:

Person::getAge;

Person 類(lèi)中定義的方法 getAge 的方法引用。

然后我們可以使用 Function 對(duì)象進(jìn)行操作:

// 獲取 getAge 方法的 Function 對(duì)象
Function getAge = Person::getAge;
// 傳參數(shù)調(diào)用 getAge 方法
Integer age = getAge.apply(p);

我們引用 getAge,然后將其應(yīng)用于正確的參數(shù)。

目標(biāo)引用的參數(shù)類(lèi)型是 Function,T 表示傳入類(lèi)型,R 表示返回類(lèi)型。比如,表達(dá)式 person -> person.getAge();,傳入?yún)?shù)是 person,返回值是 person.getAge(),那么方法引用 Person::getAge 就對(duì)應(yīng)著 Function 類(lèi)型。

5. 什么是功能接口(Functional interface)

在 Java 中,功能接口(Functional interface)指只有一個(gè)抽象方法的接口。

java.lang.Runnable 是一個(gè)功能接口,在 Runnable 中只有一個(gè)方法的聲明 void run()。我們使用匿名內(nèi)部類(lèi)實(shí)例化功能接口的對(duì)象,而使用 Lambda 表達(dá)式,可以簡(jiǎn)化寫(xiě)法。

每個(gè) Lambda 表達(dá)式都可以隱式地分配給功能接口。例如,我們可以從 Lambda 表達(dá)式創(chuàng)建 Runnable 接口的引用,如下所示:

Runnable r = () -> System.out.println("hello world");

當(dāng)我們不指定功能接口時(shí),這種類(lèi)型的轉(zhuǎn)換會(huì)被編譯器自動(dòng)處理。例如:

new Thread(
    () -> System.out.println("hello world")
).start();

在上面的代碼中,編譯器會(huì)自動(dòng)推斷,Lambda 表達(dá)式可以從 Thread 類(lèi)的構(gòu)造函數(shù)簽名(public Thread(Runnable r) { })轉(zhuǎn)換為 Runnable 接口。

@FunctionalInterface 是在 Java 8 中添加的一個(gè)新注解,用于指示接口類(lèi)型,聲明接口為 Java 語(yǔ)言規(guī)范定義的功能接口。Java 8 還聲明了 Lambda 表達(dá)式可以使用的功能接口的數(shù)量。當(dāng)您注釋的接口不是有效的功能接口時(shí), @FunctionalInterface 會(huì)產(chǎn)生編譯器級(jí)錯(cuò)誤。

以下是自定義功能接口的示例:

package com.wuxianjiezh.demo.lambda;

@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();
}

正如其定義所述,功能接口只能有一個(gè)抽象方法。如果我們嘗試在其中添加一個(gè)抽象方法,則會(huì)拋出編譯時(shí)錯(cuò)誤。例如:

package com.wuxianjiezh.demo.lambda;

@FunctionalInterface
public interface WorkerInterface {

    public void doWork();
    public void doMoreWork();
}

錯(cuò)誤:

Error:(3, 1) java: 意外的 @FunctionalInterface 注釋
  com.wuxianjiezh.demo.lambda.WorkerInterface 不是函數(shù)接口
    在 接口 com.wuxianjiezh.demo.lambda.WorkerInterface 中找到多個(gè)非覆蓋抽象方法

一旦定義了功能接口,我們就可以利用 Lambda 表達(dá)式調(diào)用。例如:

package com.wuxianjiezh.demo.lambda;

@FunctionalInterface
public interface WorkerInterface {

    public void doWork();
}

class WorkTest {

    public static void main(String[] args) {
        // 通過(guò)匿名內(nèi)部類(lèi)調(diào)用
        WorkerInterface work = new WorkerInterface() {
            @Override
            public void doWork() {
                System.out.println("通過(guò)匿名內(nèi)部類(lèi)調(diào)用");
            }
        };
        work.doWork();
        
        // 通過(guò) Lambda 表達(dá)式調(diào)用
        // Lambda 表達(dá)式實(shí)際上是一個(gè)對(duì)象。
        // 我們可以將 Lambda 表達(dá)式賦值給一個(gè)變量,就可像其它對(duì)象一樣調(diào)用。
        work = ()-> System.out.println("通過(guò) Lambda 表達(dá)式調(diào)用");
        work.doWork();
    }
}

運(yùn)行結(jié)果:

通過(guò)匿名內(nèi)部類(lèi)調(diào)用
通過(guò) Lambda 表達(dá)式調(diào)用
6. Lambda 表達(dá)式的例子 6.1 線(xiàn)程初始化

線(xiàn)程可以初始化如下:

// Old way
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello world");
    }
}).start();

// New way
new Thread(
    () -> System.out.println("Hello world")
).start();
6.2 事件處理

事件處理可以用 Java 8 使用 Lambda 表達(dá)式來(lái)完成。以下代碼顯示了將 ActionListener 添加到 UI 組件的新舊方式:

// Old way
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Hello world");
    }
});

// New way
button.addActionListener( (e) -> {
        System.out.println("Hello world");
});
6.3 遍例輸出(方法引用)

輸出給定數(shù)組的所有元素的簡(jiǎn)單代碼。請(qǐng)注意,還有一種使用 Lambda 表達(dá)式的方式。

// old way
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for (Integer n : list) {
    System.out.println(n);
}

// 使用 -> 的 Lambda 表達(dá)式
list.forEach(n -> System.out.println(n));

// 使用 :: 的 Lambda 表達(dá)式
list.forEach(System.out::println);
6.4 邏輯操作

輸出通過(guò)邏輯判斷的數(shù)據(jù)。

package com.wuxianjiezh.demo.lambda;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {

    public static void main(String[] args) {
        List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.print("輸出所有數(shù)字:");
        evaluate(list, (n) -> true);

        System.out.print("不輸出:");
        evaluate(list, (n) -> false);

        System.out.print("輸出偶數(shù):");
        evaluate(list, (n) -> n % 2 == 0);

        System.out.print("輸出奇數(shù):");
        evaluate(list, (n) -> n % 2 == 1);

        System.out.print("輸出大于 5 的數(shù)字:");
        evaluate(list, (n) -> n > 5);
    }

    public static void evaluate(List list, Predicate predicate) {
        for (Integer n : list) {
            if (predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }
}

運(yùn)行結(jié)果:

輸出所有數(shù)字:1 2 3 4 5 6 7 
不輸出:
輸出偶數(shù):2 4 6 
輸出奇數(shù):1 3 5 7 
輸出大于 5 的數(shù)字:6 7 
6.4 Stream API 示例

java.util.stream.Stream接口 和 Lambda 表達(dá)式一樣,都是 Java 8 新引入的。所有 Stream 的操作必須以 Lambda 表達(dá)式為參數(shù)。Stream 接口中帶有大量有用的方法,比如 map() 的作用就是將 input Stream 的每個(gè)元素,映射成output Stream 的另外一個(gè)元素。

下面的例子,我們將 Lambda 表達(dá)式 x -> x*x 傳遞給 map() 方法,將其應(yīng)用于流的所有元素。之后,我們使用 forEach 打印列表的所有元素。

// old way
List list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}

// new way
List list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);

下面的示例中,我們給定一個(gè)列表,然后求列表中每個(gè)元素的平方和。這個(gè)例子中,我們使用了 reduce() 方法,這個(gè)方法的主要作用是把 Stream 元素組合起來(lái)。

// old way
List list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);

// new way
List list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
7. Lambda 表達(dá)式和匿名類(lèi)之間的區(qū)別

this 關(guān)鍵字。對(duì)于匿名類(lèi) this 關(guān)鍵字解析為匿名類(lèi),而對(duì)于 Lambda 表達(dá)式,this 關(guān)鍵字解析為包含寫(xiě)入 Lambda 的類(lèi)。

編譯方式。Java 編譯器編譯 Lambda 表達(dá)式時(shí),會(huì)將其轉(zhuǎn)換為類(lèi)的私有方法,再進(jìn)行動(dòng)態(tài)綁定。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69911.html

相關(guān)文章

  • Java8-8-方法引用詳解

    摘要:實(shí)際上方法引用是表達(dá)式的一種語(yǔ)法糖。小結(jié)本篇全面介紹了方法引用的四種使用方式,且每種方式都有對(duì)應(yīng)一個(gè)示例來(lái)幫助大家理解。 上一篇我們?cè)敿?xì)介紹了Optional類(lèi)用來(lái)避免空指針問(wèn)題,本篇我們?nèi)媪私庖幌翵ava8中的方法引用特性。方法引用是lambda表達(dá)式的一種特殊形式,如果正好有某個(gè)方法滿(mǎn)足一個(gè)lambda表達(dá)式的形式,那就可以將這個(gè)lambda表達(dá)式用方法引用的方式表示,但是如果這...

    劉東 評(píng)論0 收藏0
  • Java8 Lamda詳解

    摘要:局部變量表達(dá)式的方法體與嵌套代碼塊有著相同的作用域。在表達(dá)式中不允許聲明一個(gè)與局部變量同名的參數(shù)或者局部變量。不可變的約束只作用在局部變量上,如果是一個(gè)實(shí)例變量或者閉合類(lèi)的靜態(tài)變量,那么不會(huì)有任何錯(cuò)誤被報(bào)告出來(lái)即使結(jié)果同樣未定義。 完整的Java學(xué)習(xí)的路線(xiàn)圖可以參考:我的編程之路--知識(shí)管理與知識(shí)體系 Lambda&Closures Java8 Lambda表達(dá)式10個(gè)示例 閉包一般指...

    anquan 評(píng)論0 收藏0
  • 轉(zhuǎn) | Java8初體驗(yàn)(一)lambda達(dá)式語(yǔ)法

    摘要:初體驗(yàn)下面進(jìn)入本文的正題表達(dá)式。接下來(lái)展示表達(dá)式和其好基友的配合。吐槽一下方法引用表面上看起來(lái)方法引用和構(gòu)造器引用進(jìn)一步簡(jiǎn)化了表達(dá)式的書(shū)寫(xiě),但是個(gè)人覺(jué)得這方面沒(méi)有的下劃線(xiàn)語(yǔ)法更加通用。 感謝同事【天錦】的投稿。投稿請(qǐng)聯(lián)系 [email protected] 本文主要記錄自己學(xué)習(xí)Java8的歷程,方便大家一起探討和自己的備忘。因?yàn)楸救艘彩莿倓傞_(kāi)始學(xué)習(xí)Java8,所以文中肯定有錯(cuò)誤和理解偏...

    Lucky_Boy 評(píng)論0 收藏0
  • 六個(gè)Python編程最受用的內(nèi)置函數(shù)使用詳解

      小編寫(xiě)這篇文章的話(huà),主要是給大家做出一個(gè)解答,解答一些Python常見(jiàn)問(wèn)題,比如關(guān)于編程函數(shù)的一些問(wèn)題,哪些函數(shù)編程是最受用的呢?下面就給大家詳細(xì)介紹一下。  合理的使用Python這門(mén)工具,能夠大大的提高其工作效率,起到事半功倍的作用?! ?.Map函數(shù)  map函數(shù)可以使用另外一個(gè)函數(shù)轉(zhuǎn)換整個(gè)可迭代對(duì)象的函數(shù),包括將字符串轉(zhuǎn)換為數(shù)字、數(shù)字的四舍五入等等?! ≈允褂胢ap函數(shù)來(lái)完成這些事...

    89542767 評(píng)論0 收藏0
  • Java核心技術(shù)教程整理,長(zhǎng)期更新

    以下是Java技術(shù)棧微信公眾號(hào)發(fā)布的關(guān)于 Java 的技術(shù)干貨,從以下幾個(gè)方面匯總。 Java 基礎(chǔ)篇 Java 集合篇 Java 多線(xiàn)程篇 Java JVM篇 Java 進(jìn)階篇 Java 新特性篇 Java 工具篇 Java 書(shū)籍篇 Java基礎(chǔ)篇 8張圖帶你輕松溫習(xí) Java 知識(shí) Java父類(lèi)強(qiáng)制轉(zhuǎn)換子類(lèi)原則 一張圖搞清楚 Java 異常機(jī)制 通用唯一標(biāo)識(shí)碼UUID的介紹及使用 字符串...

    Anchorer 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

haoguo

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<