Kotlin之@JvmOverloads、@JvmStatic、@JvmField、@JvmInli-灵析社区

IT大鲨鱼

写在前面

Kotlin代码可以经过编译器转换成VM虚拟机能识别的字节码,所以Java与Kotlin可以互相进行调用。而由于Java与Kotlin语言特性的差异,当Java调用Kotlin代码时,可以在Kotlin代码中适当增加一些注解,从而更方便的调用Kotlin代码。

@JvmOverloads

在Kotlin的方法里有多个默认参数时,如果在Java中直接调用,只能调用一个包含完整参数的方法,如果想暴露更多的重载函数给Java,可以使用@JvmOverloads 用于生成重载。对于每一个有默认值的参数,生成的重载会把当前有默认值的参数及其右边的参数都去掉,所以如果方法中所有的参数都有默认值,生成的重载函数中还会有一个无参的重载函数。

@JvmOverloads 主要用于构造函数、方法中,同时不能用于抽象方法、接口中的方法等。

· 应用在方法中

 @JvmOverloads fun method(a: Int, b: Boolean = true, c: String = "c") { }

转换成Java后:

   //1   @JvmOverloads   public final void method(int a, boolean b, @NotNull String c) {      Intrinsics.checkNotNullParameter(c, "c");   }  //2   @JvmOverloads   public final void method(int a, boolean b) {      method$default(this, a, b, (String)null, 4, (Object)null);   }  //3   @JvmOverloads   public final void method(int a) {      method$default(this, a, false, (String)null, 6, (Object)null);   }   //4: synthetic method   public static void method$default(KtAnnotation var0, int var1, boolean var2, String var3, int var4, Object var5) {      if ((var4 & 2) != 0) {         var2 = true;      }      if ((var4 & 4) != 0) {         var3 = "c";      }      var0.method(var1, var2, var3);   }

· 应用在自定义View构造函数中

class VpLoadMoreView @JvmOverloads constructor(    context: Context,    attrs: AttributeSet? = null,    defStyle: Int = 0,) : LinearLayout(context, attrs, defStyle) {}

转换成Java代码后:

public final class VpLoadMoreView extends LinearLayout {      //1   @JvmOverloads   public VpLoadMoreView(@NotNull Context context) {      this(context, (AttributeSet)null, 0, 6, (DefaultConstructorMarker)null);   }   //2   @JvmOverloads   public VpLoadMoreView(@NotNull Context context, @Nullable AttributeSet attrs) {      this(context, attrs, 0, 4, (DefaultConstructorMarker)null);   }    //3   @JvmOverloads    public VpLoadMoreView(@NotNull Context context, @Nullable AttributeSet attrs, int defStyle) {      Intrinsics.checkNotNullParameter(context, "context");      super(context, attrs, defStyle);   }   //4: synthetic method   public VpLoadMoreView(Context var1, AttributeSet var2, int var3, int var4, DefaultConstructorMarker var5) {      if ((var4 & 2) != 0) {         var2 = (AttributeSet)null;      }      if ((var4 & 4) != 0) {         var3 = 0;      }      this(var1, var2, var3);   }}

如果将注解去掉,转换成Java后:

public final class VpLoadMoreView extends LinearLayout {   //1   public VpLoadMoreView(@NotNull Context context, @Nullable AttributeSet attrs, int defStyle) {      Intrinsics.checkNotNullParameter(context, "context");      super(context, attrs, defStyle);   }   //2: synthetic method   public VpLoadMoreView(Context var1, AttributeSet var2, int var3, int var4, DefaultConstructorMarker var5) {      if ((var4 & 2) != 0) {         var2 = (AttributeSet)null;      }      if ((var4 & 4) != 0) {         var3 = 0;      }      this(var1, var2, var3);   }}

可以看到去掉了@JvmOverloads 注解,少了1个、2个参数的构造函数了,那么在Java中也不能初始化1个、2个参数的构造函数了。

@JvmStatic

@JvmStatic用于声明静态方法。在具名对象及伴生对象中使用时,既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法,如:

class KtA {    companion object {        @JvmStatic        fun invokeStatic() {}        fun invokeNoStatic() {}    }}

在Java中调用:

 public void invokeKt() {     KtA.invokeStatic(); //正确,可以直接调用     //KtA.invokeNoStatic(); //错误,这里调用不到     KtA.Companion.invokeStatic(); //正确     KtA.Companion.invokeNoStatic(); //正确 }

@JvmField

@JvmField使得编译器不再对该字段生成getter/setter并将其作为公开字段,如:

 val id1 = 100 //1  @JvmField  val id2 = 200 //2 var id3 = 300 //3  @JvmField  var id4 = 400 //4

编译成Java后:

 private final int id1 = 100; @JvmField public final int id2 = 200; private int id3 = 300; @JvmField public int id4 = 400; public final int getId1() {    return this.id1; } public final int getId3() {    return this.id3; } public final void setId3(int var1) {    this.id3 = var1; }

@JvmSynthetic

@JvmSynthetic可以修饰于方法上,控制只能在Kotlin中调用,如:

//kt代码class KtA {    @JvmSynthetic    fun visit() {}}

Java中调用:

 public void invokeKt() {     KtA clz = new KtA();     clz.visit(); //错误,这里在Java中调用不到。 }
如果想在Java中调用到Kotlin类中的方法,将@JvmSynthetic去掉即可。

@JvmName 、@JvmMultifileClass

@JvmName 注解可以生成类名;如果类名已存在,可以修改已生成的 Java 类的类名。 包名相同并且类名相同或者有相同的 @JvmName 注解有会错误,可以通过@JvmMultifileClass把他们合并到一起,如:

//A.kt@file:JvmName("generate")@file:JvmMultifileClasspackage org.ninetripodsfun getA() {}
//B.kt@file:JvmName("generate")@file:JvmMultifileClasspackage org.ninetripodsfun getB() {}

Java中调用:

org.ninetripods.generate.getA();org.ninetripods.generate.getB();

@JvmInline

@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)@MustBeDocumented@SinceKotlin("1.5")public actual annotation class JvmInline

@JvmInline在1.5.0版本引入,可以指定一个类为内联类,需结合value一起使用;在1.5.0之前使用inline关键字。

//1.5.0之前,inline标记内联类inline class Person(private val name: String = "")//1.5.0之后,@JvmInline + value 标记内联类@JvmInlinevalue class Person(private val name: String = "")

内联类构造参数中有且只能有一个成员变量,最终被内联到字节码中的value。,上述代码经过内联优化会在字节码中将Person对象转换为String值,从而由堆分配优化为栈分配。

阅读量:918

点赞量:0

收藏量:0