JvmName注解是Kotlin提供的一个可以变更编译器输出的注解,这里简单的介绍一下其使用规则。
1 2 3 4 5 6 7 8 | package com.example.jvmannotationsample import android.net.Uri fun String.toUri(): Uri { return Uri.parse(this) } |
---|
当我们在Java中调用上面的toUri方法时
1 | StringExtKt.toUri("https://droidyue.com"); |
---|
生成的 class 文件名称为
1 | ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/StringExtKt.class |
---|
1 2 3 4 5 6 7 8 9 | @file:JvmName("StringUtil") package com.example.jvmannotationsample import android.net.Uri fun String.toUri(): Uri { return Uri.parse(this) } |
---|
在Java中调用
1 | StringUtil.toUri("https://droidyue.com"); |
---|
生成的 class 文件名为
1 | ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/StringUtil.class |
---|
1 2 3 4 5 6 | package com.example.jvmannotationsample.jvm_name @JvmName("isOK") fun String.isValid(): Boolean { return isNotEmpty() } |
---|
生成的对应的class 文件,我们可以看到方法名称已经修改了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | javap -c ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/jvm_name/OnMethodSampleKt.class Compiled from "OnMethodSample.kt" public final class com.example.jvmannotationsample.jvm_name.OnMethodSampleKt { public static final boolean isOK(java.lang.String); Code: 0: aload_0 1: ldc #11 // String $this$isValid 3: invokestatic #17 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: aload_0 7: checkcast #19 // class java/lang/CharSequence 10: astore_1 11: iconst_0 12: istore_2 13: aload_1 14: invokeinterface #23, 1 // InterfaceMethod java/lang/CharSequence.length:()I 19: ifle 26 22: iconst_1 23: goto 27 26: iconst_0 27: ireturn } |
---|
所以,我们在Java代码中,可以这样调用
1 2 3 | public static void testJvmNameOnMethod() { OnMethodSampleKt.isOK(""); } |
---|
但是,我们在Kotlin代码中,还是只能使用isValid
而不是isOK
1 2 3 4 | fun testJvmNameOnMethod() { "".isValid() // "".isOK() unresolved reference } |
---|
那么问题就奇怪了,生成的class里面的方法是isOK
,怎么还能调用isValid
呢?
1 2 3 4 5 6 7 8 9 10 | javap -c ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/jvm_name/KotlinPlaygroundKt.class Compiled from "KotlinPlayground.kt" public final class com.example.jvmannotationsample.jvm_name.KotlinPlaygroundKt { public static final void testJvmNameOnMethod(); Code: 0: ldc #8 // String 2: invokestatic #14 // Method com/example/jvmannotationsample/jvm_name/OnMethodSampleKt.isOK:(Ljava/lang/String;)Z 5: pop 6: return } |
---|
是的,Kotlin编译器将isValid
在字节码层面又替换成了isOK
。
关于@JvmName作用到方法上,比较好的例子(来自Kotlin官网)是这样的
1 2 3 4 5 6 7 | fun List<String>.filterValid(): List<String> { TODO() } fun List<Int>.filterValid(): List<Int> { TODO() } |
---|
1 2 3 | ~/JVMAnnotationSample/app/src/main/java/com/example/jvmannotationsample/jvm_name/GenericList.kt: (3, 1): Platform declaration clash: The following declarations have the same JVM signature (filterValid(Ljava/util/List;)Ljava/util/List;): fun List<Int>.filterValid(): List<Int> defined in com.example.jvmannotationsample.jvm_name in file GenericList.kt fun List<String>.filterValid(): List<String> defined in com.example.jvmannotationsample.jvm_name in file GenericList.kt |
---|
上面的两个方法声明会导致Kotlin编译出错,因为
由于JVM对于泛型采取了类型擦除,List<Int>.filterValid()
和List<String>.filterValid()
实际上对应的都是List.filterValid()
所以,对应的解决方法
List<String>.filterValid()
修改成List<String>.filterValidString()
等具体修改如下所示
1 2 3 4 5 6 7 8 9 10 11 | package com.example.jvmannotationsample.jvm_name @JvmName("filterValidString") fun List<String>.filterValid(): List<String> { TODO() } @JvmName("filterValidInt") fun List<Int>.filterValid(): List<Int> { TODO() } |
---|
除此之外,@JvmName还可以作用在属性上。比如
1 2 3 4 5 | package com.example.jvmannotationsample.jvm_name @get:JvmName("x") @set:JvmName("changeX") var x: Int = 23 |
---|
在Java中对应的调用
1 2 3 4 | public static void testJvmNameOnProperty() { OnPropertiesSampleKt.changeX(111); OnPropertiesSampleKt.x(); } |
---|
在Kotlin中对应的调用
1 2 3 4 | fun testJvmNameOnProperty() { x = 1111 x } |
---|
和作用在方法上一样,其实现原理一致,具体如下面的反编译代码可见一斑。
Java调用处的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | javap -c ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/jvm_name/OnPropertiesSampleKt.class Compiled from "OnPropertiesSample.kt" public final class com.example.jvmannotationsample.jvm_name.OnPropertiesSampleKt { public static final int x(); Code: 0: getstatic #11 // Field x:I 3: ireturn public static final void changeX(int); Code: 0: iload_0 1: putstatic #11 // Field x:I 4: return static {}; Code: 0: bipush 23 2: putstatic #11 // Field x:I 5: return } |
---|
Kotlin调用处的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 | javap -c ./app/build/tmp/kotlin-classes/debug/com/example/jvmannotationsample/jvm_name/KotlinPlaygroundKt.class Compiled from "KotlinPlayground.kt" public final class com.example.jvmannotationsample.jvm_name.KotlinPlaygroundKt { public static final void testJvmNameOnProperty(); Code: 0: sipush 1111 3: invokestatic #36 // Method com/example/jvmannotationsample/jvm_name/OnPropertiesSampleKt.changeX:(I)V 6: invokestatic #40 // Method com/example/jvmannotationsample/jvm_name/OnPropertiesSampleKt.x:()I 9: pop 10: return } |
---|