Kotlin字節碼生成機制詳盡分析


通過注解修改Kotlin的class文件名:

對於Kotlin文件在編譯之后生成的class文件名默認是有一定規則的,比如:

而其實這個生成字節碼的文件名稱是可以被改的,之前https://www.cnblogs.com/webor2006/p/11530600.html也提及到,也就是可以用JvmName注解,再來試一下:

然后重新的build一次,再來看一下生成的class文件:

既然可以手動的將編譯的字符碼文件名給改了,那。。如果兩個Kotlin文件都指定同一個名稱,會有啥情況發生呢?試試,再來建一個Kotlin文件:

那。。有沒有一種機制能將這兩個類的內容生成到一個HelloWorld.class,當然有,也就是Kotlin中可以將多個Kotlin文件合並成一個字節碼文件,下面來看一下:

接下來再次編譯:

居然此時就沒報錯了,那這個字節碼文件中的內容是?跟進去瞅一下:

bogon:kotlin_lecture xiongwei$ javap -c -v com/kotlin/test11/HelloWorld.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/HelloWorld.class
  Last modified 2019-9-20; size 860 bytes
  MD5 checksum 47cd63b80d95d489afa25bedd346da23
public final class com.kotlin.test11.HelloWorld
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Utf8               com/kotlin/test11/HelloWorld
   #2 = Class              #1             // com/kotlin/test11/HelloWorld
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               getMystr
   #6 = Utf8               ()Ljava/lang/String;
   #7 = Utf8               Lorg/jetbrains/annotations/NotNull;
   #8 = Utf8               com/kotlin/test11/HelloWorld__HelloKotlin3Kt
   #9 = Class              #8             // com/kotlin/test11/HelloWorld__HelloKotlin3Kt
  #10 = NameAndType        #5:#6          // getMystr:()Ljava/lang/String;
  #11 = Methodref          #9.#10         // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.getMystr:()Ljava/lang/String;
  #12 = Utf8               setMystr
  #13 = Utf8               (Ljava/lang/String;)V
  #14 = NameAndType        #12:#13        // setMystr:(Ljava/lang/String;)V
  #15 = Methodref          #9.#14         // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.setMystr:(Ljava/lang/String;)V
  #16 = Utf8               <set-?>
  #17 = Utf8               Ljava/lang/String;
  #18 = Utf8               myPrint
  #19 = Utf8               ()V
  #20 = NameAndType        #18:#19        // myPrint:()V
  #21 = Methodref          #9.#20         // com/kotlin/test11/HelloWorld__HelloKotlin3Kt.myPrint:()V
  #22 = Utf8               myPrint2
  #23 = Utf8               com/kotlin/test11/HelloWorld__HelloKotlin4Kt
  #24 = Class              #23            // com/kotlin/test11/HelloWorld__HelloKotlin4Kt
  #25 = NameAndType        #22:#19        // myPrint2:()V
  #26 = Methodref          #24.#25        // com/kotlin/test11/HelloWorld__HelloKotlin4Kt.myPrint2:()V
  #27 = Utf8               Lkotlin/Metadata;
  #28 = Utf8               mv
  #29 = Integer            1
  #30 = Integer            15
  #31 = Utf8               bv
  #32 = Integer            0
  #33 = Integer            3
  #34 = Utf8               k
  #35 = Integer            4
  #36 = Utf8               d1
  #37 = Utf8               Code
  #38 = Utf8               LineNumberTable
  #39 = Utf8               RuntimeInvisibleAnnotations
  #40 = Utf8               LocalVariableTable
  #41 = Utf8               RuntimeInvisibleParameterAnnotations
  #42 = Utf8               RuntimeVisibleAnnotations
{
  public static final java.lang.String getMystr();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=1, locals=0, args_size=0
         0: invokestatic  #11                 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.getMystr:()Ljava/lang/String;
         3: areturn
      LineNumberTable:
        line 1: 0
    RuntimeInvisibleAnnotations:
      0: #7()

  public static final void setMystr(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokestatic  #15                 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.setMystr:(Ljava/lang/String;)V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0 <set-?>   Ljava/lang/String;
    RuntimeInvisibleParameterAnnotations:
      0:
        0: #7()

  public static final void myPrint();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=0, locals=0, args_size=0
         0: invokestatic  #21                 // Method com/kotlin/test11/HelloWorld__HelloKotlin3Kt.myPrint:()V
         3: return
      LineNumberTable:
        line 1: 0

  public static final void myPrint2();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=0, locals=0, args_size=0
         0: invokestatic  #26                 // Method com/kotlin/test11/HelloWorld__HelloKotlin4Kt.myPrint2:()V
         3: return
      LineNumberTable:
        line 1: 0
}
RuntimeVisibleAnnotations:
  0: #27(#28=[I#29,I#29,I#30],#31=[I#29,I#32,I#33],#34=I#35,#36=[s#8,s#23])

很明顯字節碼中確實是已經包含HelloKotlin3.kt和HelloKotlin4.kt的內容進行了合並了,既然這倆文件最終會編譯成一個字節碼,那如果在HelloKotlin4.kt中也定義一個跟HelloKotlin3.kt一樣的方法名會有啥反應呢,如下:

比較容易理解,還是將其方法名進行還原,接下來新建一個Java文件來調用一下:

@JvmField:

使用@JvmFiled注解對Kotlin中的屬性進行標注時,表示它是一個實例字段(instance filed),Kotlin編譯器在處理的時候,將不會給這個字段生成getter/setter。

下面先來新建一個類:

接下來咱們可以使用@JvmField注解,如下:

注上它之后,其實該字段就變為了一個實例字段,Kotlin編譯器就不會給該字段生成getter和setter了,下面反編譯一下:

那。。沒法驗證所說的理論了呀,這里換一種思路,從Java調用上面來驗證:

 

當然,實際過程中貌似木有必要使用該注解,一般用getter和setter的較大,做個了解。

伴生對像:

關於它其實在之前https://www.cnblogs.com/webor2006/p/11210181.html已經學習過了,這里從Java調用Kotlin的角度再來看一它:

然后再新建一個Java:

完整調用如下:

好,接下來咱們再用一個@JvmField注解:

此時程序調用就得修改為:

 

接下來再來看一下在伴生對象中定義方法又是如何調用的,如下:

那在字節碼中MyClass2是如何定義的呢?反編譯一下,之前反編譯亂碼了,下面用javap -v來看一下:

bogon:kotlin_lecture xiongwei$ javap -verbose com/kotlin/test11/MyClass2.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/MyClass2.class
  Last modified 2019-9-21; size 770 bytes
  MD5 checksum b7bf58035d08f2c1f25542b389451e7e
  Compiled from "HelloKotlin7.kt"
public final class com.kotlin.test11.MyClass2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Utf8               com/kotlin/test11/MyClass2
   #2 = Class              #1             // com/kotlin/test11/MyClass2
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = NameAndType        #5:#6          // "<init>":()V
   #8 = Methodref          #4.#7          // java/lang/Object."<init>":()V
   #9 = Utf8               this
  #10 = Utf8               Lcom/kotlin/test11/MyClass2;
  #11 = Utf8               <clinit>
  #12 = Utf8               Companion
  #13 = Utf8               Lcom/kotlin/test11/MyClass2$Companion;
  #14 = Utf8               Lkotlin/Metadata;
  #15 = Utf8               mv
  #16 = Integer            1
  #17 = Integer            15
  #18 = Utf8               bv
  #19 = Integer            0
  #20 = Integer            3
  #21 = Utf8               k
  #22 = Utf8               d1
  #23 = Utf8               
                           \n\n\ 20:B¢¨
  #24 = Utf8               d2
  #25 = Utf8
  #26 = Utf8               kotlin_lecture
  #27 = Utf8               com/kotlin/test11/MyClass2$Companion
  #28 = Class              #27            // com/kotlin/test11/MyClass2$Companion
  #29 = Utf8               (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #30 = NameAndType        #5:#29         // "<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #31 = Methodref          #28.#30        // com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #32 = NameAndType        #12:#13        // Companion:Lcom/kotlin/test11/MyClass2$Companion;
  #33 = Fieldref           #2.#32         // com/kotlin/test11/MyClass2.Companion:Lcom/kotlin/test11/MyClass2$Companion;
  #34 = Utf8               HelloKotlin7.kt
  #35 = Utf8               Code
  #36 = Utf8               LineNumberTable
  #37 = Utf8               LocalVariableTable
  #38 = Utf8               InnerClasses
  #39 = Utf8               SourceFile
  #40 = Utf8               RuntimeVisibleAnnotations
{
  public static final com.kotlin.test11.MyClass2$Companion Companion;
    descriptor: Lcom/kotlin/test11/MyClass2$Companion;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public com.kotlin.test11.MyClass2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/kotlin/test11/MyClass2;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: new           #28                 // class com/kotlin/test11/MyClass2$Companion
         3: dup
         4: aconst_null
         5: invokespecial #31                 // Method com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
         8: putstatic     #33                 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
        11: return
}
InnerClasses:
     public static final #12= #28 of #2; //Companion=class com/kotlin/test11/MyClass2$Companion of class com/kotlin/test11/MyClass2
SourceFile: "HelloKotlin7.kt"
RuntimeVisibleAnnotations:
  0: #14(#15=[I#16,I#16,I#17],#18=[I#16,I#19,I#20],#21=I#16,#22=[s#23],#24=[s#10,s#25,s#6,s#12,s#26])

木有亂碼了,好,面目就可以揭開了,首先:

所以我們就可以這樣來調用:

其實我們可以改變其伴身對象方法的默認規則,就得用下面這個注解了。

@JvmStatic:

在Kotlin中,我們可以將具名對象或是伴生對象中定義的函數注解為@JvmStatic,這樣編譯器既會在相應對象的類中生成靜態方法,也會在對象自身中生成實例方法。下面來嘗試一下:

此時咱們的調用方式就可以這樣寫了:

很明顯,這個test2肯定是一個靜態方法了,再來反編譯看一下:

bogon:kotlin_lecture xiongwei$ javap -verbose com/kotlin/test11/MyClass2.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/kotlin_lecture/out/production/kotlin_lecture/com/kotlin/test11/MyClass2.class
  Last modified 2019-9-21; size 858 bytes
  MD5 checksum 7b8819a8b4e7417db633e619b794273f
  Compiled from "HelloKotlin7.kt"
public final class com.kotlin.test11.MyClass2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Utf8               com/kotlin/test11/MyClass2
   #2 = Class              #1             // com/kotlin/test11/MyClass2
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = NameAndType        #5:#6          // "<init>":()V
   #8 = Methodref          #4.#7          // java/lang/Object."<init>":()V
   #9 = Utf8               this
  #10 = Utf8               Lcom/kotlin/test11/MyClass2;
  #11 = Utf8               <clinit>
  #12 = Utf8               Companion
  #13 = Utf8               Lcom/kotlin/test11/MyClass2$Companion;
  #14 = Utf8               Lkotlin/Metadata;
  #15 = Utf8               mv
  #16 = Integer            1
  #17 = Integer            15
  #18 = Utf8               bv
  #19 = Integer            0
  #20 = Integer            3
  #21 = Utf8               k
  #22 = Utf8               d1
  #23 = Utf8               
                           \n\n\ 20:B¢¨
  #24 = Utf8               d2
  #25 = Utf8
  #26 = Utf8               kotlin_lecture
  #27 = Utf8               test2
  #28 = Utf8               Lkotlin/jvm/JvmStatic;
  #29 = NameAndType        #12:#13        // Companion:Lcom/kotlin/test11/MyClass2$Companion;
  #30 = Fieldref           #2.#29         // com/kotlin/test11/MyClass2.Companion:Lcom/kotlin/test11/MyClass2$Companion;
  #31 = Utf8               com/kotlin/test11/MyClass2$Companion
  #32 = Class              #31            // com/kotlin/test11/MyClass2$Companion
  #33 = NameAndType        #27:#6         // test2:()V
  #34 = Methodref          #32.#33        // com/kotlin/test11/MyClass2$Companion.test2:()V
  #35 = Utf8               (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #36 = NameAndType        #5:#35         // "<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #37 = Methodref          #32.#36        // com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
  #38 = Utf8               HelloKotlin7.kt
  #39 = Utf8               Code
  #40 = Utf8               LineNumberTable
  #41 = Utf8               LocalVariableTable
  #42 = Utf8               RuntimeVisibleAnnotations
  #43 = Utf8               InnerClasses
  #44 = Utf8               SourceFile
{
  public static final com.kotlin.test11.MyClass2$Companion Companion;
    descriptor: Lcom/kotlin/test11/MyClass2$Companion;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public com.kotlin.test11.MyClass2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/kotlin/test11/MyClass2;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: new           #32                 // class com/kotlin/test11/MyClass2$Companion
         3: dup
         4: aconst_null
         5: invokespecial #37                 // Method com/kotlin/test11/MyClass2$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
         8: putstatic     #30                 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
        11: return

  public static final void test2();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #30                 // Field Companion:Lcom/kotlin/test11/MyClass2$Companion;
         3: invokevirtual #34                 // Method com/kotlin/test11/MyClass2$Companion.test2:()V
         6: return
    RuntimeVisibleAnnotations:
      0: #28()
}
InnerClasses:
     public static final #12= #32 of #2; //Companion=class com/kotlin/test11/MyClass2$Companion of class com/kotlin/test11/MyClass2
SourceFile: "HelloKotlin7.kt"
RuntimeVisibleAnnotations:
  0: #14(#15=[I#16,I#16,I#17],#18=[I#16,I#19,I#20],#21=I#16,#22=[s#23],#24=[s#10,s#25,s#6,s#12,s#26])

此時確實在MyClass2字節碼中生成了一個靜態的test2()方法了,而它的實現很明顯可以看到,它最終是調用了Companion.test2(),其根本就是由該@JvmStatic注解發生了作用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM