日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java 8系列之Lambda表达式

發布時間:2025/3/21 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 8系列之Lambda表达式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

使用Lambda表達式也有一段時間了,有時候用的云里霧里的,是該深入學習Java 8新特性的時候了。作為Java最大改變之一的Lambda表達式,其是Stream的使用基礎,那就以它開始吧。

這里,我們先明確需要解決的問題:

  • 什么是閉包?
  • Lambda表達式如何寫?
  • 什么是函數接口?
  • 類型推斷在Lambda中的體現。
  • Lambda表達式

    lambda表達式的語法由參數列表、->和函數體組成。函數體既可以是一個表達式,也可以是一個語句塊:

    • 表達式:表達式會被執行然后返回執行結果。
    • 語句塊:語句塊中的語句會被依次執行,就像方法中的語句一樣——?
      • return語句會把控制權交給匿名方法的調用者
      • break和continue只能在循環中使用
    • 如果函數體有返回值,那么函數體內部的每一條路徑都必須返回值

    表達式函數體適合小型lambda表達式,它消除了return關鍵字,使得語法更加簡潔。

    Lambda表達式的變體

    不包含參數且主體為表達式

    Lambda表達式不包含參數,使用空括號 ()表示沒有參數。

    OnClickListener mListener = () -> System.out.println("do on Click");

    該Lambda表達式實現了OnClickListener接口,該接口也只有一個doOnClick方法,沒有參數,且返回類型為void。

    public interface OnClickListener {void doOnClick(); }

    不包含參數且主體為代碼段

    該Lambda表達式實現了OnClickListener接口,其主體為一段代碼段,在其內用返回或拋出異常來退出。 只有一行代碼的Lambda表達式也可使用大括號, 用以明確Lambda表達式從何處開始、到哪里結束。

    ? ? OnClickListener mListener_ = () -> {System.out.println("插上電源");System.out.println("打開電視");};

    包含一個參數且主體為表達式

    Lambda表達式可以包含一個參數,將參數寫在()內,如果只有一個參數可以將()省略。

    OnItemClickListener mItemListener = position -> System.out.println("position = [" + position + "]");

    該Lambda表達式實現了OnItemClickListener接口,該接口也只有一個doItemClickListener方法,其參數為int類型,且返回值為void。

    public interface OnItemClickListener {void doItemClickListener(int position); }

    包含多個參數且主體為表達式

    Lambda表達式可以包含多個參數,將參數寫在()內,此時()不可以省略。

    IMathListener mPlusListener = (x, y) -> x + y; int sum = mPlusListener.doMathOperator(10, 5);

    該Lambda表達式實現了IMathListener接口,該接口只有一個doMathOperator方法,其參數為(int, int)類型,且返回值為int類型。

    public interface IMathListener {int doMathOperator(int start, int plusValue); }

    包含多個參數且主體為代碼段

    該Lambda表達式實現了IMathListener接口,該接口只有一個doMathOperator方法,在實現其方法時,創建了一個函數,用來處理結果。

    ? ? IMathListener mMaxListener = (x, y) -> {if (x > y) {return x;} else {return y;}};

    包含多個參數,指定參數類型且主體為代碼段

    該Lambda表達式實現了IMathListener接口,在實現時指定了參數類型,此時,調用時方法時的參數類型是指定的,只能傳入相應的類型的參數,若不傳入相應參數,編譯時會報錯。

    ? ? IMathListener mSubListener = (int x, int y) -> x - y;

    盡管與之前相比, Lambda表達式中的參數需要的樣板代碼很少,但是Java 8仍然是一種靜態類型語言。

    引用值, 而不是變量

    在使用內部類時,我們總是碰到這種情況,需要引用內部類外面的變量,比如其所在方法內的變量,或者該類的全局變量。當使用方法內的變量時,需要將變量聲明為final。此時,將變量聲明為final, 意味著不能為其重復賦值,同時在匿名內部,實際上是用的使用賦給該變量的一個特定的值。

    final String name = getUserName(); button.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent event) {System.out.println("hi " + name);} });

    在Java 8中對放松了這限制,在匿名內部,可以引用其所在方法內的非final變量,但是該變量在既成事實上必須是final,也就是說該變量只能賦值一次。如果再次對其賦值,編譯器會報錯。

    現在,我們暫且將在匿名內部類內使用的其所在方法內的變量命名為A,不管是在匿名類內部還是在匿名類所在的方法內,再次對A進行賦值時,編譯器都會報如下錯誤,其意思是變量A是在內部類中訪問的,需要聲明為final或有效的final類型。Variable ‘plusFinal’ is accessed from within inner class, needs to be final or effectively final

    在Lambda表達式中,也是同樣的問題,對于其方法體內引用的外部變量,在Lambda表達式所在方法內對變量再次賦值時,編譯器會報同樣的錯誤。也就是意味著,換句話說,Lambda表達式引用的是值,而不是變量

    這種行為也解釋了為什么Lambda表達式也被稱為閉包。未賦值的變量與周邊環境隔離起來,進而被綁定到一個特定的值。在Java 8中引入了閉包這一概念,并將其使用在了Lambda表達式中。眾說紛紜的計算機編程語言圈子里,Java是否擁有真正的閉包一直備受爭議,因為在 Java 中只能引用既成事實上的final變量。可以肯定的是,Lambda表達式都是靜態類型。

    閉包在現在的很多流行的語言中都存在,例如 C++、C# 。閉包允許我們創建函數指針,并把它們作為參數傳遞。

    函數接口

    函數式接口是什么呢?函數式接口(Functional Interface)是Java 8對一類特殊類型的接口的稱呼。這類接口只定義了唯一的抽象方法的接口(除了隱含的Object對象的公共方法),用作Lambda表達式的類型。

    從函數接口的定義可以看出,首先要明確的,其是一個接口,而這個接口呢,有且只有一個抽象的方法,那怎么又和函數結合在一起了呢?

    public interface IMathListener {int doMathOperator(int start, int plusValue); }

    我們先看一個例子,對于IMathListener接口,這個接口只有一個抽象方法doMathOperator,其接收兩個int類型的參數,返回值為int,這個接口可以稱為是一個函數接口。當我們聲明其對象時,我們可以這樣做:

    IMathListener mSubListener = (x, y) -> x - y; mMaxListener.doMathOperator(10, 5));// 其值:5

    剛才的聲明,就是用Lambda表達式聲明了IMathListener的實現,其實現的意義是求兩個傳入值的差值。這個例子說明了,函數接口可以通過Lambda表達式來實現。下面來看它是如何和函數扯上關系的。

    public class Math {public static int doIntPlus(int start, int plusValue) {return start + plusValue;} }

    現有一個Math類,其內聲明了一個靜態方法doIntPlus,該方法接收兩個int類型的參數,返回值為int,也就是說doIntPlus與IMathListener接口中的doMathOperator方法的簽名一樣。既然簽名一樣,我們可以搞些什么事情呢。往下看:

    IMathListener mPlusListener = Math::doIntPlus;

    我們通過函數調用,直接生成了一個IMathListener對象,這里寫法不了解的,后續會做介紹,看下Java 8中的引用。我們還是接著說,通過方法引用來支持Lambda表達式。這樣現有函數、接口及Lambda表達式完美的結合在一起。

    從前面已經知道,Lambda表達式都是靜態類型的,也就是說其在編譯時就已經被編譯,所以剛才被引用的方法必須是靜態的,否則編譯器會報錯。

    Non-static method cannot be referenced from a static context

    非靜態方法不能從靜態上下文引用

    對于函數接口而言,接口中唯一方法的命名并不重要了,只要方法簽名和Lambda表達式的類別相匹配即可。當然了,為了增加代碼的易讀性,只需在函數接口中為參數起一個代表意義的名字即可。

    為了更形象的聲明接口,我們可以使用圖形來描述不同類型接口。指向函數接口的箭頭表示參數, 如果箭頭從函數接口射出, 則表示方法的返回類型。若接口沒有返回值,沒有箭頭從函數接口射出。?


    這里,我們應該對函數接口有了清晰的認識。對于一個函數接口而言,其應該有以下特性:

    • 只具有一個方法的接口
    • 其可以被隱式轉換為lambda表達式
    • 現有靜態方法可以支持lambda表達式
    • 每個用作函數接口的接口都應添加 @FunctionalInterface注解
    @FunctionalInterface public interface IMathListener {int doMathOperator(int start, int plusValue); }

    該注解會強制 javac 檢查一個接口是否符合函數接口的標準。 如果該注解添加給一個枚舉?
    類型、 類或另一個注解, 或者接口包含不止一個抽象方法, javac 就會報錯。 重構代碼時,?
    使用它能很容易發現問題。

    類型推斷

    關于類型推斷,我們在Java 7中,已經不止一次用到了,可能你一直都沒有注意到。比如創建一個ArrayList,我們可以這么做:

    ArrayList<String> mArrayA = new ArrayList<String>(); ArrayList<String> mArrayB = new ArrayList<>();

    在創建mArrayA時,明確指定了ArrayList為String類型,而在創建mArrayB時并未指定ArrayList的類型,編譯器是如何知道mArrayB的數據類型呢?在Java 7中,有個神奇的<>操作符,它可使javac推斷出泛型參數的類型,這樣不用明確聲明泛型類型,編譯器就可以自己推斷出來,這就是它的神奇之處!

    對于一個傳遞的參數,編輯器也可以根據參數的類型來推斷具體傳入的參數的數據類型。比如有一個方法updateList,其參數為一個String的ArrayList,在調用該方法時,我們傳入了一個新建的ArrayList但未指定ArrayList的數據類型,此時編輯器會自行推斷傳入的ArrayList的數據類型為String,

    public void updateList(ArrayList<String> values);updateList(new ArrayList<>());

    Lambda表達式中的類型推斷,實際上是Java 7就引入的目標類型推斷的擴展。javac根據Lambda 表達式上下文信息就能推斷出參數的正確類型。 程序依然要經過類型檢查來保證運行的安全性, 但不用再顯式聲明類型罷了,這就是所謂的類型推斷。

    目標類型是指Lambda表達式所在上下文環境的類型。比如,將 Lambda 表達式賦值給一個局部變量,或傳遞給一個方法作為參數,局部變量或方法參數的類型就是 Lambda 表達式的目標類型

    以之前提到的IMathListener為例,在下面表達式中,javac會自行將x和y推斷為int類型.

    IMathListener mSubListener = (x, y) -> x - y;

    而在實際開發過程中,為了接口方法的通用性,一般都是使用泛型來指定參數的類型,比如Funtion接口,該接口接收一個F類型的參數并返回一個T類型的值。

    Function<String, Integer> string2Integer = Integer::valueOf;?

    在這個實例中,javac可以推斷出接收的數據類型為String,返回類型為Integer。盡管類型推斷已經相當智能,但是其也不是無所不能的。在其自行推斷前,你需給出其推斷的標注。比如下面的例子,javac并不能夠推斷出Function的具體數據類型:

    Function string2Integer = Integer::valueOf;?

    上述代碼,編譯都不會通過,編譯器給出的報錯信息如下:?
    Operator ‘& #x002B;’ cannot be applied to java.lang.Object, java.lang.Object.

    大家都知道泛型的擦除原則,在編譯時,編譯器會擦除泛型的具體類型。從而,此時編譯器認為參數和返回值都是java.lang.Object實例。這已經偏離了我們的思想,就算編譯可以通過,也會造成后續邏輯的混亂,從而不知道該行代碼,到底在做什么。在使用泛型時,我們一定會指定泛型的具體的數據類型,以作為編譯器的類型推斷的標準。

    方法重載帶來的煩惱

    在Java中可以重載方法,造成多個方法有相同的方法名,但簽名卻不一樣,盡管這樣讓多態性展現的淋漓盡致,但是對于類型推斷,帶來了不少的煩惱,因為javac可能會推斷出多種類型。 這時, javac會挑出最具體的類型。比如方法overloadedMethod中,參數類型不同,返回值相同,這是一個典型的方法重載,在使用具體類型調用時,java可以根據具體類型來判斷,此時控制臺應打印“String”。

    overloadedMethod("abc");private void overloadedMethod(Object o) {System.out.print("Object"); } private void overloadedMethod(String s) {System.out.print("String"); }

    如果我們參數傳遞的是Lambda表達式呢?下面的表達式中,編譯器并不知道x和y的數據類型,也并未指定具體的類型,必然造成編譯異常。

    overloadedMethod((x)->y);

    如果在Lambda表達式中指定返回值的數據類型,編譯器可以清晰的知道overloadedMethod的參數類型為String類型,根據具體的數據類型,從而調用overloadedMethod(String s) 方法,避免了類型推斷不明確的問題。

    overloadedMethod((x)->(String)y);

    總而言之,Lambda表達式作為參數時,其類型由它的目標類型推導得出,推導過程遵循如下規則:

    • 如果只有一個可能的目標類型,由相應函數接口里的參數類型推導得出;
    • 如果有多個可能的目標類型,由最具體的類型推導得出;
    • 如果有多個可能的目標類型且最具體的類型不明確, 則需人為指定類型。

    總結

    Lambda是函數式編程的基礎,而函數式編程是技術的發展方向。作為一個成熟的Java開發人員,學習新的編程技術那是必須的,也是值得花時間學習的。

    大量的使用Lambda表達式,盡管避免了大量的使用匿名內部類,提高了代碼的可讀性,可是對猿人們要求更高了,應當對相應的接口或者框架有一定的熟悉程度,否則,看代碼就活在云里霧里了。這也是自我相逼提升的一種方式吧。

    參考文檔

  • Java中的閉包與回調
  • 深入理解Java閉包概念
  • 一見鐘情!Java閉包
  • Java 8 函數式接口
  • 深入理解Java 8 Lambda(語言篇——lambda,方法引用,目標類型和默認方法)
  • ---------------------?
    作者:行云間?
    來源:CSDN?
    原文:https://blog.csdn.net/io_field/article/details/54380200?
    版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

    總結

    以上是生活随笔為你收集整理的Java 8系列之Lambda表达式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 深爱五月激情网 | 精品国产乱码久久久久久预案 | 国产日韩欧美综合 | 四虎影院色| 91网站在线免费观看 | 男人用嘴添女人下身免费视频 | 奇米91| 国产看片网站 | 综合成人| 久久婷婷激情 | 热久久久久久 | 亚洲男人天堂2017 | 2021国产精品视频 | 亚洲另类天堂 | 蜜桃啪啪 | 国产一区视频在线播放 | 丝袜一区二区三区 | 久久久久久久女国产乱让韩 | 2025国产精品 | 国产黑丝一区 | 亚洲一区二区在线免费观看 | 亚洲精品国产片 | 日韩欧美一区二区三区在线观看 | 国内精品久久久久久久影视简单 | 夜夜爽爽| 成人在线免费观看网站 | 色综合久久五月 | 久久综合伊人77777麻豆最新章节 | 国产做爰xxxⅹ高潮视频12p | 波多av在线 | 亚洲一级片免费 | 一区二区中文在线 | 2019中文在线观看 | 国产艳情片 | 婷婷色六月 | 欧美成人精品欧美一 | 超碰免费在 | 91午夜视频在线观看 | 99爱免费 | 在线射 | 日韩熟女精品一区二区三区 | 99热这里只有精品99 | 理论在线视频 | 欧美一二三区 | 男人都懂的网站 | 91视频免费在线 | 成人午夜性视频 | 日韩怡春院 | 尤物在线观看视频 | 欧美性爱视频久久 | 久久成人综合 | 玖玖在线播放 | 九一亚洲精品 | 欧美在线视频播放 | 99久久久久无码国产精品 | av在线电影网站 | 黑人操亚洲人 | 精品人妻一区二区三区浪潮在线 | 日韩香蕉视频 | 综合天天 | av在线黄色 | 性高湖久久久久久久久免费 | 国产成人精品片 | 亚洲人体一区 | 久久久久久五月天 | 9色在线视频 | 国产精品午夜影院 | 欧美亚洲一区二区三区 | 婷婷亚洲五月色综合 | 天堂在线观看视频 | 少妇性高潮视频 | 人人妻人人玩人人澡人人爽 | 午夜免费视频网站 | 日韩精品自拍偷拍 | 黄色国产在线 | 男生和女生一起差差差很痛的视频 | 国产精品自拍99 | 青青国产精品 | 五月激情丁香婷婷 | 91综合久久 | 成年人网站黄色 | 中国特级毛片 | 俄罗斯毛片基地 | 国产成人av一区二区三区在线观看 | 少妇不卡视频 | 日本午夜精品理论片a级app发布 | 精品国产一区二区三区噜噜噜 | 深夜福利视频在线观看 | 免费无码不卡视频在线观看 | 特种兵之深入敌后 | 成人午夜毛片 | 海角社区id:1220.7126,10. | 国产一区二区在线免费观看 | 日批免费在线观看 | 久久久无码一区二区三区 | 国产av第一区 | 六月丁香激情 | 91最新入口| 草免费视频 |