【Kotlin】Kotlin 与 Java 互操作 ② ( @JvmField 注解字段给 Java | @JvmOverloads 注解修饰函数 | @JvmStatic 注解声明静态成员 )

一、使用 @JvmField 注解暴露 Kotlin 字段给 Java


1、Java 类中通过 Getter 和 Setter 方法访问 Kotlin 字段

在 Java 中是 不能直接访问 Kotlin 中的字段 的 , 必须 调用相应的 Getter 和 Setter 方法 , 才能进行访问 ;

代码示例 :

Kotlin 类 : 在 Kotlin 中声明的成员属性 , 默认就是 private 私有属性 , 默认为其生成了 Getter 和 Setter 方法 ;

class Hello {
    var name = "Tom"
}

Java 类直接调用 : 在 Java 类中 , 不能直接调用 Kotlin 字段 ;

public class HelloJava {
    public static void main(String[] args) {
        Hello hello = new Hello();
        System.out.println(hello.name);
    }
}

在 Java 类中会报错 :

'name' has private access in 'Hello'

在这里插入图片描述

在 Java 类中 , 只能通过 Getter 和 Setter 方法 , 调用 Kotlin 字段 ;

public class HelloJava {
    public static void main(String[] args) {
        Hello hello = new Hello();
        System.out.println(hello.getName());
    }
}

执行结果 :
在这里插入图片描述

2、Java 类中直接访问被 @JvmField 注解修饰的 Kotlin 字段

如果在 Kotlin 中 , 使用 @JvmField 注解 修饰 成员属性 , 其作用是将 Kotlin 字段暴露给 Java , 在 Java 中可以不使用 Getter 和 Setter 方法 而直接访问 Kotlin 字段 ;

Kotlin 代码 :

class Hello {
    @JvmField
    var name = "Tom"
}

Java 代码 :

public class HelloJava {
    public static void main(String[] args) {
        Hello hello = new Hello();
        System.out.println(hello.name);
    }
}

执行结果 :

在这里插入图片描述

@JvmField 注解 相当于 将 Kotlin 中的字段声明为 Java 字段 , 此时 Kotlin 不会为该字段自动生成 Getter 和 Setter 方法 ;

二、使用 @JvmOverloads 注解修饰 Kotlin 函数


在 Kotlin 中 , 函数参数 可以 自带默认值 , 调用时可以 直接传入 想要的参数即可 ;

但是在 Java 调用 Kotlin 函数 中 , Java 语言不支持 函数参数 自带默认值的 语法 , 如果传入指定的参数 , 就需要对函数进行重载 ;

在 Kotlin 中 使用 @JvmOverloads 注解修饰 Kotlin 函数 , 会自动 为 Java 用户实现 一系列的 重载函数 ;
如 : 参数列表是 ( String , age ) , 使用 @JvmOverloads 注解修饰该函数 , 会自动生成

  • 0 个参数 ,
  • 1 个参数 ,
  • 2 个参数

的函数 ;

1、Kotlin 默认参数函数调用示例

Kotlin 代码示例 : 在下面的 helloStudent 函数中 , 两个参数都设置了默认参数值 , Kotlin 中调用该函数 , 可以传入 0 , 1 , 2 个参数 , 其中传入 1 个参数还可以选择传入哪个参数 ;

class Hello {
    fun helloStudent(name: String = "Tom", age: Int = 18) {
        println("Student $name is $age years old , say hello !")
    }
}

fun main() {
    var hello = Hello();
    hello.helloStudent()
    hello.helloStudent("Jerry")
    hello.helloStudent(age = 22)
    hello.helloStudent("Bill", 12)
}

执行结果 :

Student Tom is 18 years old , say hello !
Student Jerry is 18 years old , say hello !
Student Tom is 22 years old , say hello !
Student Bill is 12 years old , say hello !

在这里插入图片描述

分析上述 Kotlin 代码的字节码数据 , 在 Kotlin Bytecode 中查看字节码数据 , 反编译成 Java 代码内容如下 :

// Hello.java
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"u0000u001enu0002u0018u0002nu0002u0010u0000nu0002bu0002nu0002u0010u0002nu0000nu0002u0010u000enu0000nu0002u0010bnu0000u0018u00002u00020u0001Bu0005¢u0006u0002u0010u0002Ju001au0010u0003u001au00020u00042bbu0002u0010u0005u001au00020u00062bbu0002u0010u0007u001au00020b¨u0006t"},
   d2 = {"LHello;", "", "()V", "helloStudent", "", "name", "", "age", "", "KotlinDemo"}
)
public final class Hello {
   public final void helloStudent(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      String var3 = "Student " + name + " is " + age + " years old , say hello !";
      boolean var4 = false;
      System.out.println(var3);
   }

   // $FF: synthetic method
   public static void helloStudent$default(Hello var0, String var1, int var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = "Tom";
      }

      if ((var3 & 2) != 0) {
         var2 = 18;
      }

      var0.helloStudent(var1, var2);
   }
}
// HelloKt.java
import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"u0000bnu0000nu0002u0010u0002nu0000u001au0006u0010u0000u001au00020u0001¨u0006u0002"},
   d2 = {"main", "", "KotlinDemo"}
)
public final class HelloKt {
   public static final void main() {
      Hello hello = new Hello();
      Hello.helloStudent$default(hello, (String)null, 0, 3, (Object)null);
      Hello.helloStudent$default(hello, "Jerry", 0, 2, (Object)null);
      Hello.helloStudent$default(hello, (String)null, 22, 1, (Object)null);
      hello.helloStudent("Bill", 12);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

2、Java 中调用 Kotlin 默认参数函数

如果 在 Java 代码中 , 想要 像 Kotlin 那样传入任意个数和类型的参数 , 就需要使用 函数重载实现 ;

如果 直接像 Kotlin 中那样调用 , 肯定会报错 :
在这里插入图片描述

使用 @JvmOverloads 注解修饰 Kotlin 函数 , 会自动为 Java 用户实现 一系列的 重载函数 ;

Kotlin 代码示例 :

class Hello {
    @JvmOverloads
    fun helloStudent(name: String = "Tom", age: Int = 18) {
        println("Student $name is $age years old , say hello !")
    }
}

fun main() {
    var hello = Hello();
    hello.helloStudent()
    hello.helloStudent("Jerry")
    hello.helloStudent(age = 22)
    hello.helloStudent("Bill", 12)
}

Java 代码示例 :

public class HelloJava {
    public static void main(String[] args) {
        Hello hello = new Hello();
        hello.helloStudent();
        hello.helloStudent("Jerry");
        hello.helloStudent("Bill", 12);
    }
}

执行结果 :

Student Tom is 18 years old , say hello !
Student Jerry is 18 years old , say hello !
Student Bill is 12 years old , say hello !

在这里插入图片描述

分析上述 使用了 @JvmOverloads 注解 的 Kotlin 类对应的字节码数据 , 将字节码反编译回 Java 代码 , 内容如下 :

// Hello.java
import kotlin.Metadata;
import kotlin.jvm.JvmOverloads;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"u0000u001enu0002u0018u0002nu0002u0010u0000nu0002bu0002nu0002u0010u0002nu0000nu0002u0010u000enu0000nu0002u0010bnu0000u0018u00002u00020u0001Bu0005¢u0006u0002u0010u0002Ju001cu0010u0003u001au00020u00042bbu0002u0010u0005u001au00020u00062bbu0002u0010u0007u001au00020bHu0007¨u0006t"},
   d2 = {"LHello;", "", "()V", "helloStudent", "", "name", "", "age", "", "KotlinDemo"}
)
public final class Hello {
   @JvmOverloads
   public final void helloStudent(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      String var3 = "Student " + name + " is " + age + " years old , say hello !";
      boolean var4 = false;
      System.out.println(var3);
   }

   // $FF: synthetic method
   public static void helloStudent$default(Hello var0, String var1, int var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = "Tom";
      }

      if ((var3 & 2) != 0) {
         var2 = 18;
      }

      var0.helloStudent(var1, var2);
   }

   @JvmOverloads
   public final void helloStudent(@NotNull String name) {
      helloStudent$default(this, name, 0, 2, (Object)null);
   }

   @JvmOverloads
   public final void helloStudent() {
      helloStudent$default(this, (String)null, 0, 3, (Object)null);
   }
}
// HelloKt.java
import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"u0000bnu0000nu0002u0010u0002nu0000u001au0006u0010u0000u001au00020u0001¨u0006u0002"},
   d2 = {"main", "", "KotlinDemo"}
)
public final class HelloKt {
   public static final void main() {
      Hello hello = new Hello();
      Hello.helloStudent$default(hello, (String)null, 0, 3, (Object)null);
      Hello.helloStudent$default(hello, "Jerry", 0, 2, (Object)null);
      Hello.helloStudent$default(hello, (String)null, 22, 1, (Object)null);
      hello.helloStudent("Bill", 12);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

使用了 @JvmOverloads 注解后 ,
在编译时 , 自动为 helloStudent 函数 , 生成了 0 , 1, 2 个参数的重载函数 ,
这样在 Java 中调用时 , 可以直接调用这些方法 ;

   @JvmOverloads
   public final void helloStudent(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      String var3 = "Student " + name + " is " + age + " years old , say hello !";
      boolean var4 = false;
      System.out.println(var3);
   }

   @JvmOverloads
   public final void helloStudent(@NotNull String name) {
      helloStudent$default(this, name, 0, 2, (Object)null);
   }

   @JvmOverloads
   public final void helloStudent() {
      helloStudent$default(this, (String)null, 0, 3, (Object)null);
   }

三、使用 @JvmStatic 注解声明静态成员


在 Kotlin 中 , 没有静态成员概念 , 需要声明静态成员时 , 一般都在其 Companion 伴生对象中声明 ;

在 Java 中 调用 Kotlin 的 Companion 伴生对象 中的成员时 , 需要通过如下形式进行调用 :

Kotlin.Companion.成员属性
Kotlin.Companion.成员函数

如果想要 在不使用 Companion 的前提下 直接调用 Kotlin 中的 Companion 伴生对象 成员 ,

可以 在 companion object 中 ,

使用 @JvmStatic 注解 将伴生对象中的成员 声明 为 Java 静态成员 ,

Java 中可以按照静态成员的方式进行访问 ;

1、Java 正常访问 Kotlin 伴生对象成员

在下面的代码中 , 在 Java 语言中访问 Kotlin 伴生对象成员 , 需要先获取 Hello.Companion 类的伴生对象 , 然后再访问 伴生对象 中的成员 ;

Kotlin 代码 :

class Hello {
    companion object {
        var name = "Tom"
        fun say() {
            println("Hello World")
        }
    }
}

Java 代码 :

public class HelloJava {
    public static void main(String[] args) {
        System.out.println(Hello.Companion.getName());
        Hello.Companion.say();
    }
}

执行结果 :

Tom
Hello World

在这里插入图片描述

查看该 Kotlin 类生成的字节码 反编译 的 Java 代码 :

import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"u0000fnu0002u0018u0002nu0002u0010u0000nu0002bu0003u0018u0000 u00032u00020u0001:u0001u0003Bu0005¢u0006u0002u0010u0002¨u0006u0004"},
   d2 = {"LHello;", "", "()V", "Companion", "KotlinDemo"}
)
public final class Hello {
   @NotNull
   private static String name = "Tom";
   public static final Hello.Companion Companion = new Hello.Companion((DefaultConstructorMarker)null);

   @Metadata(
      mv = {1, 1, 16},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"u0000u001anu0002u0018u0002nu0002u0010u0000nu0002bu0002nu0002u0010u000enu0002bu0005nu0002u0010u0002nu0000bu0086u0003u0018u00002u00020u0001Bu0007bu0002¢u0006u0002u0010u0002Ju0006u0010tu001au00020nRu001au0010u0003u001au00020u0004Xu0086u000e¢u0006u000enu0000u001au0004bu0005u0010u0006"u0004bu0007u0010b¨u0006u000b"},
      d2 = {"LHello$Companion;", "", "()V", "name", "", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "say", "", "KotlinDemo"}
   )
   public static final class Companion {
      @NotNull
      public final String getName() {
         return Hello.name;
      }

      public final void setName(@NotNull String var1) {
         Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
         Hello.name = var1;
      }

      public final void say() {
         String var1 = "Hello World";
         boolean var2 = false;
         System.out.println(var1);
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

在 Kotlin 编译成的字节码数据中 , name 成员属性 和 say 成员函数 , 都定义在了 Hello.Companion 伴生对象中 , 如果要访问 这两个成员 , 必须通过 Hello.Companion 进行访问 ;

2、Java 以静态方式访问 Kotlin 伴生对象成员

在下面的代码中 , 使用 @JvmStatic 注解修饰 Kotlin 中伴生对象中的成员 , 则可以在 Java 中 以静态方式访问这些成员 ;

Kotlin 代码 :

class Hello {
    companion object {
        @JvmStatic
        var name = "Tom"

        @JvmStatic
        fun say() {
            println("Hello World")
        }
    }
}

Java 代码 :

public class HelloJava {
    public static void main(String[] args) {
        System.out.println(Hello.getName());
        Hello.say();
    }
}

执行结果 :
在这里插入图片描述

查看该 Kotlin 类生成的字节码 反编译 的 Java 代码 :

import kotlin.Metadata;
import kotlin.jvm.JvmStatic;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"u0000fnu0002u0018u0002nu0002u0010u0000nu0002bu0003u0018u0000 u00032u00020u0001:u0001u0003Bu0005¢u0006u0002u0010u0002¨u0006u0004"},
   d2 = {"LHello;", "", "()V", "Companion", "KotlinDemo"}
)
public final class Hello {
   @NotNull
   private static String name = "Tom";
   public static final Hello.Companion Companion = new Hello.Companion((DefaultConstructorMarker)null);

   @NotNull
   public static final String getName() {
      Hello.Companion var10000 = Companion;
      return name;
   }

   public static final void setName(@NotNull String var0) {
      Hello.Companion var10000 = Companion;
      name = var0;
   }

   @JvmStatic
   public static final void say() {
      Companion.say();
   }

   @Metadata(
      mv = {1, 1, 16},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"u0000u001anu0002u0018u0002nu0002u0010u0000nu0002bu0002nu0002u0010u000enu0002bu0006nu0002u0010u0002nu0000bu0086u0003u0018u00002u00020u0001Bu0007bu0002¢u0006u0002u0010u0002Jbu0010nu001au00020u000bHu0007R$u0010u0003u001au00020u00048u0006@u0006Xu0087u000e¢u0006u0014nu0000u0012u0004bu0005u0010u0002u001au0004bu0006u0010u0007"u0004bbu0010t¨u0006f"},
      d2 = {"LHello$Companion;", "", "()V", "name", "", "name$annotations", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "say", "", "KotlinDemo"}
   )
   public static final class Companion {
      /** @deprecated */
      // $FF: synthetic method
      @JvmStatic
      public static void name$annotations() {
      }

      @NotNull
      public final String getName() {
         return Hello.name;
      }

      public final void setName(@NotNull String var1) {
         Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
         Hello.name = var1;
      }

      @JvmStatic
      public final void say() {
         String var1 = "Hello World";
         boolean var2 = false;
         System.out.println(var1);
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

Kotlin 类编译 时 , 自动生成了

  • Hello.name 静态成员 以及 其 静态的 Getter 和 Setter 方法 ,
  • Hello.say 静态方法 ;

这 两个静态 成员都是 Kotlin 类中的 Hello.Companion 伴生对象 中的成员 , 但是编译时生成在了 Hello 类中 , 称为了 Hello 类的成员 ;

public final class Hello {
   @NotNull
   private static String name = "Tom";

   @NotNull
   public static final String getName() {
      Hello.Companion var10000 = Companion;
      return name;
   }

   public static final void setName(@NotNull String var0) {
      Hello.Companion var10000 = Companion;
      name = var0;
   }

   @JvmStatic
   public static final void say() {
      Companion.say();
   }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>