1. 程式人生 > >Java JDK1.8新特性Lambda 表示式

Java JDK1.8新特性Lambda 表示式

一、lambda含義

    lambda表示數學符號“λ”,計算機領域中λ代表“λ演算”,表達了計算機中最基本的概念:“呼叫”和“置換”。在很多動態語言和C#中都有相應的lambda語法,這類語法都為了簡化程式碼,提高執行效率。

二、lambda 專案的背景參考這裡

    無論是面嚮物件語言還是函式式語言,基本數值都可以被動態的封裝入程式動作:面嚮物件語言通過“方法”,函式式語言通過“函式。

    介於“方法”和“函式”的定義有很多種,補充下IBM知識庫的解釋

在面嚮物件語言中,方法不是一階值(First-class value),在函式式語言中,函式是一階值。在函式式語言中,函式可以作為另一個函式的返回值或引數,還可以作為一個變數的值,函式可以巢狀定義,而在面嚮物件語言中的的“方法”做不到這點。

    Java可以說是面嚮物件語言的代表,如果要呼叫其方法,需要先建立物件。不過Java物件都是“重量級”的,例項化具體的類的物件,需要經過定義和申明兩個階段。比如定義方法,並給內部欄位賦初始值。但是一個物件只包含一個方法的情況很多,比如實現API中的“回撥介面”功能的類,在swing中有介面:

Java程式碼  收藏程式碼
  1. <span><span><span style="">public interface ActionListener {   
  2.     void actionPerformed(ActionEvent e);  
  3. }</span></span></span>  

   現有的實現方式大多是:

Java程式碼  收藏程式碼
  1. <span><span><span style="">button.addActionListener(new ActionListener() {   
  2.   public void actionPerformed(ActionEvent e) {   
  3.     ui.dazzle(e.getModifiers());  
  4.   }  
  5. });</span></span></span>  

    很多現有的類庫都基於這種設計實現,所以對於程式碼被明確定義執行在單獨執行緒的API來說,匿名內部類尤為重要。這些匿名內部類只存在於建立它的執行緒中。但是在平行計算領域,CPU的製造商著力發展多核技術來提升CPU的功能,這麼做幾乎無法依靠多核的優勢來提升其效能。

    鑑於回撥函式和其他功能式語法的關係越來越密切,所以必須建立儘可能的輕量級的資料模型(從編碼角度而言,效能方面下文再說)。對於這點來說匿名內部類的缺點如下:

1.語法相對複雜。

2.在呼叫內部類的上下文中,指引和this的指代容易混淆。

3.類載入和例項建立語法不可避免。

4.不能引用外部的非final物件。

5.不能抽象化控制流程

    針對這些問題,lambda專案致力於

1. 消除問題1和問題2,通過引入更簡單的表示式和區域性變數的定義規則。

2. 迴避問題3,定義更靈活更友善的語法。這裡只是迴避,類載入和例項化本身不可避免。下文會解釋。

3. 改善問題4,允許使用者使用最終有效的區域性變數。

    不過lambda專案目前並不能解決所有關於內部類的問題。問題4和問題5沒有完全解決,這計劃在將類版本中繼續改善。對於效能方面,原文也沒有提,不過後面有些補充。

三、lambda用法

    通過上文可以瞭解到,lambda語法是針對“回撥介面”和“匿名內部類”作出的改進,所以lambda的語法目前僅對於部分介面,這些介面的特點是隻含一個抽象方法,在lambda專案中,早期稱為SAM型別(SAM = single abstract method 即單一抽象方法)。在最新的文件中(即這個版本),它們有了新名字,叫函式介面(functional interface),比如:

1java.lang.Runnable 2java.util.concurrent.Callable 3java.security.PrivilegedAction 4java.util.Comparator 5java.io.FileFilter 6java.nio.file.PathMatcher 7java.lang.reflect.InvocationHandler 8java.beans.PropertyChangeListener 9java.awt.event.ActionListener 10javax.swing.event.ChangeListener lambda的語法包括三部分 1、引數列表 2、箭頭符號"->" 3、程式碼塊。

    其中程式碼塊很像一個方法體,return語句將控制權交還給匿名方法(anonymous method,即lambda表示式)的呼叫者;break和continue不能出現在函式體的頂部,不過可以出現在內部的迴圈裡;如果程式碼塊得出最終結果,那麼每一個控制路徑(control path) 必須都有返回或丟擲異常。

如果程式碼塊只有簡單一行,可以省略return關鍵字和“{}”符號(以下所寫的例子都是基於JDK 1.8 lambda預覽版),比如:

Java程式碼  收藏程式碼
  1. <span><span><span style="">public class LambdaTest {      
  2.     public static void main(String... args) {  
  3.         //這裡有{}和return 以及 ;  
  4.         Runnable r = () -> { System.out.println("hello world"); };  
  5.         //這裡不需要{}和return  
  6.         java.util.Comparator<String> c = (String s1, String s2) -> s2.length()-s1.length();          
  7.         r.run();  
  8.         System.out.println(c.compare("s1""12323"));  
  9.     }  
  10. }</span></span></span>  

輸出為:

hello world

3

除了這些現有介面,我們還可以自定義函式介面:

Java程式碼  收藏程式碼
  1. <span><span><span style="">public class LambdaTest {  
  2.     interface lambdaInterface {  
  3.         public void me(String str);  
  4.     }  
  5.     public static void main(String... args) {  
  6.         lambdaInterface li = (String s)->{System.out.println(s);};  
  7.         li.me("hello world!");  
  8.     }  
  9. }</span></span></span>  

 輸出為:

hello world!

    新的lambda方法從語法上的確是簡化了很多。和lambda第一次釋出的語法相比也優雅很多。

四、lambda程式碼塊的位元組碼

看完了語法的確很簡單,那麼lambda是怎麼實現的,就得從位元組碼考察了。這裡和匿名內部類做個對比,編譯如下程式碼:

Java程式碼  收藏程式碼
  1. <span><span><span style="">public class LambdaTest {  
  2.     lambdaInterface li1 = ()->{System.out.println(this);};  
  3.     lambdaInterface li2 = new lambdaInterface(){  
  4.             public void me(){  
  5.                 System.out.println(this);  
  6.             }  
  7.         };  
  8.     public static void main(String... args) {  
  9.         LambdaTest lt = new LambdaTest();  
  10.         lt.li1.me();  
  11.         lt.li2.me();  
  12.     }  
  13. }  
  14. interface lambdaInterface {  
  15.     public void me();  
  16. }</span></span></span>  

編譯後有會有四個檔案:

LambdaTest.class          

LambdaTest$1.class      

LambdaTest$2.class

lambdaInterface.class    

它的的輸出結果為:

[email protected]

[email protected]

結果很明顯地顯示,lambda程式碼塊和內部類的this的指引是不一樣的。lambda程式碼塊輸出的是呼叫者的this,即lambdaTest.class的例項。匿名內部類輸出的是自己lambdaTest$1.class的例項。

先看看lambdaTest.class的位元組碼:

Txt程式碼  收藏程式碼
  1. <span><span><span style="">public class LambdaTest {  
  2.   lambdaInterface li1;  
  3.   lambdaInterface li2;  
  4.   public LambdaTest();  
  5.     Code:  
  6.        0: aload_0  
  7.        1: invokespecial #1                  // Method java/lang/Object."<init>":()V  
  8.        4: aload_0  
  9.        5: new           #2                  // class LambdaTest$2  
  10.        8: dup  
  11.        9: aload_0  
  12.       10: aload_0  
  13.       11: invokespecial #3                  // Method LambdaTest$2."<init>":(LLambdaTest;LLambdaTest;)V  
  14.       14: putfield      #4                  // Field li1:LlambdaInterface;  
  15.       17: aload_0  
  16.       18: new           #5                  // class LambdaTest$1  
  17.       21: dup  
  18.       22: aload_0  
  19.       23: invokespecial #6                  // Method LambdaTest$1."<init>":(LLambdaTest;)V  
  20.       26: putfield      #7                  // Field li2:LlambdaInterface;  
  21.       29: return  
  22.   public static void main(java.lang.String...);  
  23.     Code:  
  24.        0: new           #8                  // class LambdaTest  
  25.        3: dup  
  26.        4: invokespecial #9                  // Method "<init>":()V  
  27.        7: astore_1  
  28.        8: aload_1  
  29.        9: getfield      #4                  // Field li1:LlambdaInterface;  
  30.       12: invokeinterface #10,  1           // InterfaceMethod lambdaInterface.me:()V  
  31.       17: aload_1  
  32.       18: getfield      #7                  // Field li2:LlambdaInterface;  
  33.       21: invokeinterface #10,  1           // InterfaceMethod lambdaInterface.me:()V  
  34.       26: return  
  35. }</span></span></span>  

   從這裡可以看出,lambda程式碼塊和匿名內部類的呼叫方法是一樣的,都是先建立一個方法,然後建立其控制代碼,這兩個控制代碼分別對應lambdaTest$2.class和lambdaTest$1.class。

   其中匿名內部類對應LambdaTest$1,它的位元組碼為:

Txt程式碼  收藏程式碼
  1. <span><span><span style="">class LambdaTest$1 implements lambdaInterface {  
  2.   final LambdaTest this$0;  
  3.   LambdaTest$1(LambdaTest);  
  4.     Code:  
  5.        0: aload_0  
  6.        1: aload_1  
  7.        2: putfield      #1                  // Field this$0:LLambdaTest;  
  8.        5: aload_0  
  9.        6: invokespecial #2                  // Method java/lang/Object."<init>":()V  
  10.        9: return  
  11.   public void me();  
  12.     Code:  
  13.        0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;  
  14.        3: aload_0  
  15.        4: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V  
  16.        7: return  
  17. }</span></span></span>  

    lambda程式碼塊對應LambdaTest$2,它的位元組碼為:

Txt程式碼  

相關推薦

Java JDK1.8特性Lambda 表示式

一、lambda含義     lambda表示數學符號“λ”,計算機領域中λ代表“λ演算”,表達了計算機中最基本的概念:“呼叫”和“置換”。在很多動態語言和C#中都有相應的lambda語法,這類語法都為了簡化程式碼,提高執行效率。 二、lambda 專案的背景,參考

java1.8特性-lambda表示式

什麼是lambda表示式 長期以來,java為了保持簡單性和一致性,拒絕給變數賦值成“一段程式碼”,如果你想把“一段程式碼”賦給一個Java變數,應該怎麼做呢?這個“一段程式碼”就是lambda表示式。 為什麼引入lambda表示式 lambo表示式是一個可傳

java1.8 特性 - Lambda表示式

排序介面優化 先來體驗一下lambda最直觀的優點:簡潔程式碼   //匿名內部類   Comparator<Integer> cpt = new Comparator<Integer>() {    &nbs

JDK1.8特性(二)Lambda表示式入門

lambda表示式本質是匿名方法,下面是一些lambda表示式: (int x, int y) -> x + y () -> 42 (String s) -> { System.out.println(s); } 第一個lambda表示式接收x和y這兩個整形引

Java--8--特性--Lambda

value 需要 員工信息 span final oid function get test java9 都出來了,我才開始接觸到java8的新特性,有點脫節啊。。 Lambda是一個匿名函數,可以理解為一段可以傳遞的代碼,將代碼像數據一樣傳遞,下面是一個小例子。 pub

Java 8 特性 - Lambda表達式(一)

ava 鏈接 article post lambda targe dash lambda表達式 java8 鏈接 Java8新特性——Lambda表達式(一)Java 8 新特性 - Lambda表達式(一)

jdk1.8特性lambda表達式

rest 簡潔 cnblogs ensure 1.8 可能 finish main RF   lambda表達式其實就是指一個匿名函數,應用最廣泛的就是匿名內部類。在jdk1.8之前,我們定義一個匿名內部類可能需要寫一大坨代碼,現在有了lambda之後,可以寫的很簡潔了。但

Java基礎入門之jdk1.8特性

Lamda 表示式(目標型別) 簡介 語法糖,也叫糖衣語法 指的是計算機中 新增某種語法 這種語法 ,能使程式設計師更加方便的使用語言開發程式,同時,增強了程式碼的可讀性 避免了出錯的機會,但是,這種語法對於語言的功能並且有增強 例如: 泛型 自動裝箱拆箱 增強

Java基礎(五):JDK1.8特性

JDK1.8新特性 lambda表示式 Lambda lambda作用:lambda是一個語法糖,簡化匿名內部類的使用。 lambda使用條件 引數或者變數必須是介面 介面中只包含一個抽象方法 lambda格式 (引數型別 引數名稱 …)-> { 程

Java還要再學一遍基礎(四)JDK1.8特性default,static

JDK1.8新特性default,static用法 在1.8以前,我們的Interface之中通常除了抽象方法別的什麼都沒有,但是從1.8引入開始Interface中還可以有具體的實現!其中所要用到的兩個非常重要的關鍵字就是:default和static

Java日期學習筆記(二):JDK1.8特性

Java 8另一個新增的重要特性就是引入了新的時間和日期API,它們被包含在java.time包中。藉助新的時間和日期API可以以更簡潔的方法處理時間和日期。 在介紹本篇文章內容之前,我們先來討論Java 8為什麼要引入新的日期API,與之前的時間和日期處理方式有什麼不同?

jdk1.8特性 : 接口中可以有普通方法(非靜態方法)和靜態方法 , 顛覆了之前我的理解 : 接口中只能有共有常量和抽象方法的概念,後面必須要加一句jdk1.7和1..7之前

@override 編譯 sys 接口 blank new style nts highlight 看到jdk某些接口中存在default方法,於是... http://shaomeng95.iteye.com/blog/998820 為什麽接口只能是公有常量? p

jdk1.8特性之接口default方法

sent arguments sel 可能 beyond lar iter none 裏的   眾所周知,default是java的關鍵字之一,使用場景是配合switch關鍵字用於條件分支的默認項。但自從java的jdk1.8橫空出世以後,它就被賦予了另一項很酷的能力——在

jdk1.8特性應用之Iterable

accep sub 抽象 default describe dem generate using 操作   我們繼續看lambda表達式的應用: public void urlExcuAspect(RpcController controller, Message

jdk1.7/jdk1.8特性

在面試中經常會問jdk1.6,jdk1.7,jdk1.8的區別: 最近面試的時候的面試官問我jdk8的新特性: 我回答了幾個,他提問到:為什麼defualt沒有被推廣,有什麼弊端。 我很蒙圈,在這裡新增下回答: 我目前還不知道 jdk1.7新增特性: 1.7新特性轉載 1 s

(Java)jdk-8 - 特性 - 介面

介面: Java 8允許我們給介面新增一個非抽象的方法實現,只需要使用default關鍵字即可,這個又叫做擴充套件方法 注意:現在介面還可以【存在靜態方法】,可以使用 介面名.靜態方法名 的形式直接呼叫。 例子: public class Test{ public static

jdk8 特性lambda表示式

首先說明下,jdk8的新特性forEach,Stream在遍歷集合時,程式碼看上去簡潔些,但效率會變低 好了,今天開始介紹新特性,先說lambda表示式 Python裡有個匿名函式,用lambda定義,jdk8的lambda表示式就相當於一個匿名函式,只是功能強大一些 先說一個概念:

java8特性 (Lambda表示式)

1:lambda表示式入門       提供的介面有四種:                      ->Function<T,R>接

Jdk1.8特性

Lambda表示式:Lambda 允許把函式作為一個方法的引數(函式作為引數傳遞進方法中)。可以使程式碼變的更加簡潔緊湊。       Lambda表示式的實質其實還是匿名內部類,而匿名內部類在訪問外部區域性變數時,要求變數必須宣告為final!不過我們在使用Lambda表示

三、java8特性 lambda表示式在stream中的應用

1.關於JSR335 JSR是Java Specification Requests的縮寫,意思是Java 規範請求,Java 8 版本的主要改進是 Lambda 專案(JSR 335),其目的是使 Java 更易於為多核處理器編寫程式碼。JSR 335=lambda表示式