偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

和Lambdas的第一次親密接觸

開發(fā) 后端
在這篇文章里,我的目的是要看看面紗后的東西 ——看看在運(yùn)行時環(huán)境里lambdas是表現(xiàn)的,在方法的調(diào)度過程中涉及到哪些字節(jié)碼指令。

java 8

Lambda工程是即將到來的Java8的一大主題,可能也是程序員們最期待已久的東西。隨著Java lambdas的到來,還有一個有趣的東西被附帶的加進(jìn)了Java語言——defender(守衛(wèi)者)方法。在這篇文章里,我的目的是要看看面紗后的東西 ——看看在運(yùn)行時環(huán)境里lambdas是表現(xiàn)的,在方法的調(diào)度過程中涉及到哪些字節(jié)碼指令。

盡管Java 8還沒有正式發(fā)布,我們?nèi)匀豢梢韵螺d各種平臺上的早期預(yù)覽版,在其上做簡單的嘗試。

你也想試試lambdas,是嗎?

如果你熟悉其它的還有l(wèi)ambda表達(dá)式的編程語言,比如Groovy 或 Ruby,當(dāng)?shù)谝谎劭吹絁ava里的lambda時,你也許會吃驚于它的不簡單。在Java里,lambda表達(dá)式是“SAM”(Single Abstract Method)——一個含有一個抽象方法的接口(是的,現(xiàn)在接口里可以含有一個非抽象的方法,defender守衛(wèi)方法)。

舉個例子,大家熟知的Runnable接口就可以完美的被當(dāng)作一個SAM類型:

  1. Runnable r = () -> System.out.println("hello lambda!"); 

,這同樣也適用于Comparable接口:

  1. Comparator<Integer> cmp = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0); 

寫成下面的樣子也是一樣的:

  1. Comparator<Integer> cmp = (x, y) -> {  
  2.   return (x < y) ? -1 : ((x > y) ? 1 : 0);  
  3. }; 

從中可以看出,單行的lambda表達(dá)式似乎是隱含了一個return語句。

那么,如何寫一個能接受lambda表達(dá)式作為參數(shù)的方法呢?這樣,你需要先把這個參數(shù)聲明成函數(shù)式的接口,然后把lambda傳入:

  1. interface Action {  
  2.    void run(String param);  
  3. }  
  4.  
  5. public void execute(Action action){  
  6.    action.run("Hello!");  

一旦有了一個能將函數(shù)式接口作為參數(shù)的方法,我們就可以像下面這樣調(diào)用它:

  1. execute((String s) -> System.out.println(s)); 

還可以更簡潔,這個表達(dá)式可以被替換成對一個方法的引用,因?yàn)樗皇菃蝹€方法,而且它們的參數(shù)是相同的:

  1. execute(System.out::println); 

然而,如果參數(shù)上有任何其它形式的變化,我們就不能直接引用方法,必須寫全lambda表達(dá)式:

  1. execute((String s) -> System.out.println("*" + s + "*")); 

我覺得這種語法還是相當(dāng)漂亮的,現(xiàn)在,Java語言里有了一個非常優(yōu)雅的lambdas解決方案,盡管Java里并不存在函數(shù)式類型。

JDK 8里的函數(shù)式接口

我們已經(jīng)知道,lambda在運(yùn)行時的表現(xiàn)形式是一個函數(shù)式的接口(或“SAM類型”)——只有一個抽象方法的接口。盡管JDK里已經(jīng)有了不少這樣的接口,例如RunnableComparable ,它們符合這種標(biāo)準(zhǔn),但很顯然,對于一個新API的進(jìn)化來說,這是不夠的。我們不可能所有地方都用Runnables接口。

在JDK 8 里有個新包,java.util.function,里面包含了很多函數(shù)式接口,都是提供在新API里使用的。我不想把它們?nèi)谐鰜?mdash;—你們自己可以去看一下,學(xué)習(xí)一下這個新包 [[64637]]

但看起來這個新包在不斷的變化,經(jīng)常性的一些新接口會出現(xiàn)而另一些會消失。例如,以前曾有過 java.util.function.Block 這個類,最新的版本中卻沒有它,我寫這篇博客時使用的版本是:

  1. anton$ java -version  
  2. openjdk version "1.8.0-ea" 
  3. OpenJDK Runtime Environment (build 1.8.0-ea-b75)  
  4. OpenJDK 64-Bit Server VM (build 25.0-b15, mixed mode) 

我研究發(fā)現(xiàn),它現(xiàn)在被 Consumer 接口替代,collection包里的所有新方法都將使用它。例如,Collection接口里定義了forEach方法,如下:

  1. public default void forEach(Consumer<? super T> consumer) {  
  2.   for (T t : this) {  
  3.     consumer.accept(t);  
  4.   }  

Consumer接口里一個有趣地方是,它實(shí)際上定義了一個抽象方法——accept(T t)和一個defender方法——Consumer<T> chain(Consumer<? extend T> consumer)。這就是說你可以鏈?zhǔn)秸{(diào)用這個接口。我不確定如何使用,因?yàn)槲以贘DK包里沒有找到chain(..)的使用方法說明。

我還發(fā)現(xiàn)所有的接口都使用了@FunctionalInterface運(yùn)行時注注解注釋。這個注釋不僅僅是個說明,它還被javac使用來驗(yàn)證這個接口是否真是一個函數(shù)式接口,是否至少有一個抽象方法在里面。

所以,如果我們來編譯下面的這段代碼

  1. @FunctionalInterface 
  2. interface Action {  
  3.   void run(String param);  
  4.   void stop(String param);  

編譯器會告訴我們:

  1. java: Unexpected @FunctionalInterface annotation  
  2.   Action is not a functional interface 
  3.     multiple non-overriding abstract methods found in interface Action 

而下面的就能編譯通過:

  1. @FunctionalInterface 
  2. interface Action {  
  3.   void run(String param);  
  4.   default void stop(String param){}  

反編譯lambdas

我對語法語言特征其實(shí)并不是很好奇,我更好奇的是這些特征在運(yùn)行時的表現(xiàn)形式,這就是為什么我像往常一樣,拿起我喜愛的javap工具,開始查看lambdas里的這些類的字節(jié)碼。

目前(在Java 7之前),如果你想在Java里模擬lambdas,你需要定義一個匿名的內(nèi)部類。它在編譯后會產(chǎn)生一個具體的class。如果你在一段代碼里定義了多個這樣的類,你會發(fā)現(xiàn)這些類后面會跟著一些數(shù)字。那lambdas也會這樣嗎?

看看下面的這段代碼:

  1. public class Main {  
  2.  
  3.   @FunctionalInterface 
  4.   interface Action {  
  5.     Object run(String s);  
  6.   }  
  7.  
  8.   public void action(Action action){  
  9.     action.run("Hello!");  
  10.   }  
  11.  
  12.   public static void main(String[] args) {  
  13.     new Main().action((String s) -> System.out.print("*" + s + "*"));  
  14.   }  
  15.  

編譯產(chǎn)生了兩個類文件:Main.classMain$Action.class,沒有匿名類實(shí)現(xiàn)里那樣的序號化的類。那么,在Main.class里應(yīng)該會有一些東西來代表我在main方法里定義的lambdas表達(dá)式的實(shí)現(xiàn)。

  1. $ javap -p Main   
  2.  
  3. Warning: Binary file Main contains com.zt.Main  
  4. Compiled from "Main.java" 
  5. public class com.zt.Main {  
  6.   public com.zt.Main();  
  7.   public void action(com.zt.Main$Action);  
  8.   public static void main(java.lang.String[]);  
  9.   private static java.lang.Object lambda$0(java.lang.String);  

啊哈!編譯類了產(chǎn)生了lambda$0方法!使用-c -v指示符會讓我們看到真正的字節(jié)碼,以及常量池的定義。

main方法里顯示,invokedynamic被用來調(diào)度這個調(diào)用:

  1. public static void main(java.lang.String[]);  
  2.   Code:  
  3.    0new               #4    // class com/zt/Main  
  4.    3: dup            
  5.    4: invokespecial #5        // Method "":()V  
  6.    7: invokedynamic #6,  0    // InvokeDynamic #0:lambda:()Lcom/zt/Main$Action;  
  7.    12: invokevirtual #7       // Method action:(Lcom/zt/Main$Action;)V  
  8.    15return 

而在常量池里,你也可以找到運(yùn)行時的啟動方法:

  1. BootstrapMethods:  
  2.   0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  
  3.   Method arguments:  
  4.     #41 invokeinterface com/zt/Main$Action.run:(Ljava/lang/String;)Ljava/lang/Object;  
  5.     #42 invokestatic com/zt/Main.lambda$0:(Ljava/lang/String;)Ljava/lang/Object;  
  6.     #43 (Ljava/lang/String;)Ljava/lang/Object; 

你會發(fā)現(xiàn)到處都是在使用MethodHandle API,但我們現(xiàn)在不打算深入到里面。現(xiàn)在我們可以確認(rèn)一點(diǎn),我們的定義是引用了編譯出來lambda$0方法。

我很好奇,如果我定義一個相同名字的靜態(tài)方法會怎樣——畢竟“lambda$0”是一個有效的標(biāo)識符!于是,我定義了自己的lambda$0方法:

  1. public static Object lambda$0(String s){ return null; } 

而編譯失敗,編譯器不允許我在代碼了擁有這個方法:

  1. java: the symbol lambda$0(java.lang.String) conflicts with a   
  2.            compiler-synthesized symbol in com.zt.Main 

同時,如果我刪掉這段定義lambdas表達(dá)式的代碼,程序能順利編譯通過。這就是說,lambdas表達(dá)式在編譯期間會比類里的其它數(shù)據(jù)早先分析,不過這只是我的猜測。

請注意:在這個例子中,lambda并沒有去引用任何變量,也沒有引用類內(nèi)部的任何方法。這就是為什么產(chǎn)生的lambda$0方法是靜態(tài)的。如果lambdas引用了上下文中的變量或方法,那生成的將是一個非靜態(tài)方法。所以,不要被這個例子誤導(dǎo)——lambdas是可以捕獲上下文環(huán)境內(nèi)容的!

總結(jié)lambdas

我可以毫無疑問的說,lambdas和伴隨它一起的各種特征(守衛(wèi)方法(defender)
,升級的集合類庫)將很快給Java帶來巨大的沖擊。它的語法相當(dāng)?shù)暮唵危坏┏绦騿T們意識到這些功能給開發(fā)效率帶來的好處,我們將會看到大量的程序員都會運(yùn)用這個功能。

看看lambdas會編譯成什么樣子,這對于我來說是一件非常有趣的事情,我很開心,因?yàn)槲铱吹竭@些所有的invokedynamic指令調(diào)用都沒有出現(xiàn)匿名內(nèi)部類。

英文原文:Java 8: The First Taste of Lambdas

譯文鏈接:http://www.aqee.net/java-8-the-first-taste-of-lambdas/

責(zé)任編輯:林師授 來源: 外刊IT評論
相關(guān)推薦

2017-08-08 12:50:51

Serverless云端數(shù)據(jù)庫

2009-11-27 14:18:37

Scala

2010-01-29 09:01:40

.NET 4.0

2018-06-12 11:19:28

2020-05-06 07:18:59

數(shù)據(jù)中臺架構(gòu)

2015-11-03 11:39:18

清華大學(xué)OpenStackEasyStack

2012-10-19 09:27:23

Ruby

2009-05-15 08:59:32

Windows 7微軟操作系統(tǒng)

2018-08-16 11:30:12

JavaCPU緩存

2010-05-25 13:17:28

MySQL數(shù)據(jù)庫

2009-06-08 16:00:00

ASP.NET 3.5

2011-07-21 21:01:37

諾基亞塞班蘋果

2017-03-22 15:38:28

代碼架構(gòu)Java

2013-05-13 11:35:53

獨(dú)立開發(fā)開發(fā)經(jīng)驗(yàn)開發(fā)感悟

2023-04-12 08:14:10

mysql關(guān)聯(lián)字段索引

2022-03-16 14:59:28

打包debian模板文件

2012-04-13 10:11:58

Windows 8泄露

2023-09-11 00:14:46

后端團(tuán)隊(duì)項(xiàng)目

2015-08-05 10:54:49

2022-08-15 08:16:56

shiroWeb認(rèn)證
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號