程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java 8 靜態類型言語Lambda表達式完成原了解析

Java 8 靜態類型言語Lambda表達式完成原了解析

編輯:關於JAVA

Java 8 靜態類型言語Lambda表達式完成原了解析。本站提示廣大學習愛好者:(Java 8 靜態類型言語Lambda表達式完成原了解析)文章只能為提供參考,不一定能成為您想要的結果。以下是Java 8 靜態類型言語Lambda表達式完成原了解析正文


Java 8支持靜態言語,看到了很酷的Lambda表達式,對不斷以靜態類型言語自居的Java,讓人看到了Java虛擬機可以支持靜態言語的目的。

import java.util.function.Consumer; 
public class Lambda { 
  public static void main(String[] args) { 
    Consumer<String> c = s -> System.out.println(s); 
    c.accept("hello lambda!"); 
  } 
} 

剛看到這個表達式,覺得java的處置方式是屬於外部匿名類的方式

public class Lambda { 
  static { 
    System.setProperty("jdk.internal.lambda.dumpProxyClasses", "."); 
  } 
  public static void main(String[] args) { 
    Consumer<String> c = new Consumer<String>(){ 
      @Override 
      public void accept(String s) { 
        System.out.println(s); 
      } 
      }; 
    c.accept("hello lambda"); 
  } 
} 

編譯的後果應該是Lambda.class , Lambda$1.class 猜想在支持靜態言語java換湯不換藥,在最後編譯的時分生成我們罕見的方式。

但是後果不是這樣的,只是發生了一個Lambda.class

反編譯吧,來看看真相是什麼?

javap -v -p Lambda.class 

留意  -p 這個參數 -p 參數會顯示一切的辦法,而不帶默許是不會反編譯private 的辦法的

public Lambda(); 
  descriptor: ()V 
  flags: ACC_PUBLIC 
  Code: 
   stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokespecial #21         // Method java/lang/Object."<init>":()V 
     4: return 
   LineNumberTable: 
    line 3: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    5   0 this  LLambda; 
 public static void main(java.lang.String[]); 
  descriptor: ([Ljava/lang/String;)V 
  flags: ACC_PUBLIC, ACC_STATIC 
  Code: 
   stack=2, locals=2, args_size=1 
     0: invokedynamic #30, 0       // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; 
     5: astore_1 
     6: aload_1 
     7: ldc      #31         // String hello lambda 
     9: invokeinterface #33, 2      // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V 
    14: return 
   LineNumberTable: 
    line 8: 0 
    line 9: 6 
    line 10: 14 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0   15   0 args  [Ljava/lang/String; 
      6    9   1   c  Ljava/util/function/Consumer; 
   LocalVariableTypeTable: 
    Start Length Slot Name  Signature 
      6    9   1   c  Ljava/util/function/Consumer<Ljava/lang/String;>; 
 private static void lambda$0(java.lang.String); 
  descriptor: (Ljava/lang/String;)V 
  flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC 
  Code: 
   stack=2, locals=1, args_size=1 
     0: getstatic   #46         // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: aload_0 
     4: invokevirtual #50         // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     7: return 
   LineNumberTable: 
    line 8: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    8   0   s  Ljava/lang/String; 
} 
SourceFile: "Lambda.java" 
BootstrapMethods: 
 0: #66 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
  Method arguments: 
   #67 (Ljava/lang/Object;)V 
   #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
   #71 (Ljava/lang/String;)V 
InnerClasses: 
   public static final #77= #73 of #75; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles 

在這裡我們發現了幾個與我們罕見的java不太一樣的中央,由於常量定義太多了,文章中就不貼出了

1. Invokedynamic 指令

Java的調用函數的四大指令(invokevirtual、invokespecial、invokestatic、invokeinterface),通常辦法的符號援用在靜態類型言語編譯時就能發生,而靜態類型言語只要在運轉期才干確定接納者類型,改動四大指令的語意對java的版本有很大的影響,所以在JSR 292 《Supporting Dynamically Typed Languages on the Java Platform》添加了一個新的指令

Invokedynamic

0: invokedynamic #30,  0   // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; 

 #30 是代表常量#30 也就是前面的正文InvokeDynamic #0:accept:()Ljava/util/function/Consumer;

0 是占位符號,目前無用

2. BootstrapMethods

每一個invokedynamic指令的實例叫做一個靜態調用點(dynamic call site), 靜態調用點最開端是未鏈接形態(unlinked:表示還未指定該調用點要調用的辦法), 靜態調用點依托引導辦法來鏈接到詳細的辦法.  引導辦法是由編譯器生成, 在運轉期當JVM第一次遇到invokedynamic指令時, 會調用引導辦法來將invokedynamic指令所指定的名字(辦法名,辦法簽名)和詳細的執行代碼(目的辦法)鏈接起來, 引導辦法的前往值永世的決議了調用點的行為.引導辦法的前往值類型是java.lang.invoke.CallSite, 一個invokedynamic指令關聯一個CallSite, 將一切的調用委托到CallSite以後的target(MethodHandle)
InvokeDynamic #0 就是BootstrapMethods表示#0的地位

0: #66 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
 Method arguments: 
  #67 (Ljava/lang/Object;)V 
  #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
  #71 (Ljava/lang/String;)V 

我們看到調用了LambdaMetaFactory.metafactory 的辦法

參數:

LambdaMetafactory.metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)有六個參數, 按順序描繪如下

1. MethodHandles.Lookup caller : 代表查找上下文與調用者的訪問權限, 運用invokedynamic指令時, JVM會自動自動填充這個參數

2. String invokedName : 要完成的辦法的名字, 運用invokedynamic時, JVM自動幫我們填充(填充內容來自常量池InvokeDynamic.NameAndType.Name), 在這裡JVM為我們填充為 "apply", 即Consumer.accept辦法名.

3. MethodType invokedType : 調用點希冀的辦法參數的類型和前往值的類型(辦法signature). 運用invokedynamic指令時, JVM會自動自動填充這個參數(填充內容來自常量池InvokeDynamic.NameAndType.Type), 在這裡參數為String, 前往值類型為Consumer, 表示這個調用點的目的辦法的參數為String, 然後invokedynamic執行完後會前往一個即Consumer實例.

4. MethodType samMethodType :  函數對象將要完成的接口辦法類型, 這裡運轉時, 值為 (Object)Object 即 Consumer.accept辦法的類型(泛型信息被擦除).#67 (Ljava/lang/Object;)V

5. MethodHandle implMethod : 一個直接辦法句柄(DirectMethodHandle), 描繪在調用時將被執行的詳細完成辦法 (包括適當的參數適配, 前往類型適配, 和在調用參數前附加上捕捉的參數), 在這裡為 #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 辦法的辦法句柄.

6. MethodType instantiatedMethodType : 函數接口辦法交換泛型為詳細類型後的辦法類型, 通常和 samMethodType 一樣, 不同的狀況為泛型:

比方函數接口辦法定義為 void accept(T t)  T為泛型標識, 這個時分辦法類型為(Object)Void,  在編譯時T已確定, 即T由String交換, 這時samMethodType就是 (Object)Void, 而instantiatedMethodType為(String)Void.

第4, 5, 6 三個參數來自class文件中的. 如下面引導辦法字節碼中Method arguments前面的三個參數就是將使用於4, 5, 6的參數.

Method arguments: 
  #67 (Ljava/lang/Object;)V 
  #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
  #71 (Ljava/lang/String;)V 

我們來看metafactory 的辦法裡的完成代碼

public static CallSite metafactory(MethodHandles.Lookup caller, 
                    String invokedName, 
                    MethodType invokedType, 
                    MethodType samMethodType, 
                    MethodHandle implMethod, 
                    MethodType instantiatedMethodType) 
      throws LambdaConversionException { 
    AbstractValidatingLambdaMetafactory mf; 
    mf = new InnerClassLambdaMetafactory(caller, invokedType, 
                       invokedName, samMethodType, 
                       implMethod, instantiatedMethodType, 
                       false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); 
    mf.validateMetafactoryArgs(); 
    return mf.buildCallSite(); 
  } 

在buildCallSite的函數中

CallSite buildCallSite() throws LambdaConversionException { 
    final Class<?> innerClass = spinInnerClass(); 

函數spinInnerClass 構建了這個外部類,也就是生成了一個Lambda$$Lambda$1/716157500 這樣的外部類,這個類是在運轉的時分構建的,並不會保管在磁盤中,假如想看到這個構建的類,可以經過設置環境參數

System.setProperty("jdk.internal.lambda.dumpProxyClasses", "."); 

會在你指定的途徑 . 以後運轉途徑上生成這個外部類

3.靜態類

Java在編譯表達式的時分會生成lambda$0靜態公有類,在這個類裡完成了表達式中的辦法塊 system.out.println(s);

private static void lambda$0(java.lang.String); 
  descriptor: (Ljava/lang/String;)V 
  flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC 
  Code: 
   stack=2, locals=1, args_size=1 
     0: getstatic   #46         // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: aload_0 
     4: invokevirtual #50         // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     7: return 
   LineNumberTable: 
    line 8: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    8   0   s  Ljava/lang/String;

當然了在上一步經過設置的jdk.internal.lambda.dumpProxyClasses裡生成的Lambda$$Lambda$1.class

public void accept(java.lang.Object); 
  descriptor: (Ljava/lang/Object;)V 
  flags: ACC_PUBLIC 
  Code: 
   stack=1, locals=2, args_size=2 
    0: aload_1 
    1: checkcast   #15         // class java/lang/String 
    4: invokestatic #21         // Method Lambda.lambda$0:(Ljava/lang/String;)V 
    7: return 
  RuntimeVisibleAnnotations: 
   0: #13() 

調用了Lambda.lambda$0靜態函數,也就是表達式中的函數塊

總結

這樣就完成的完成了Lambda表達式,運用invokedynamic指令,運轉時調用LambdaMetafactory.metafactory靜態的生成外部類,完成了接口,外部類裡的調用辦法塊並不是靜態生成的,只是在原class裡曾經編譯生成了一個靜態的辦法,外部類只需求調用該靜態辦法

以上所述是給大家引見的Java 8 靜態類型言語Lambda表達式完成原了解析,希望對大家有所協助,假如大家有任何疑問請給我留言,會及時回復大家的。在此也十分感激大家對網站的支持!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved