通過注解修改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注解發生了作用。