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

news/2024/10/22 15:41:14/

文章目录

  • 一、使用 @JvmField 注解暴露 Kotlin 字段给 Java
    • 1、Java 类中通过 Getter 和 Setter 方法访问 Kotlin 字段
    • 2、Java 类中直接访问被 @JvmField 注解修饰的 Kotlin 字段
  • 二、使用 @JvmOverloads 注解修饰 Kotlin 函数
    • 1、Kotlin 默认参数函数调用示例
    • 2、Java 中调用 Kotlin 默认参数函数
  • 三、使用 @JvmStatic 注解声明静态成员
    • 1、Java 正常访问 Kotlin 伴生对象成员
    • 2、Java 以静态方式访问 Kotlin 伴生对象成员





一、使用 @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 {@JvmFieldvar 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 = {"\u0000\u001e\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u001a\u0010\u0003\u001a\u00020\u00042\b\b\u0002\u0010\u0005\u001a\u00020\u00062\b\b\u0002\u0010\u0007\u001a\u00020\b¨\u0006\t"},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 methodpublic 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 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},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 methodpublic static void main(String[] var0) {main();}
}

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


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

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

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


Kotlin 代码示例 :

class Hello {@JvmOverloadsfun 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 = {"\u0000\u001e\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u001c\u0010\u0003\u001a\u00020\u00042\b\b\u0002\u0010\u0005\u001a\u00020\u00062\b\b\u0002\u0010\u0007\u001a\u00020\bH\u0007¨\u0006\t"},d2 = {"LHello;", "", "()V", "helloStudent", "", "name", "", "age", "", "KotlinDemo"}
)
public final class Hello {@JvmOverloadspublic 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 methodpublic 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);}@JvmOverloadspublic final void helloStudent(@NotNull String name) {helloStudent$default(this, name, 0, 2, (Object)null);}@JvmOverloadspublic 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 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},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 methodpublic static void main(String[] var0) {main();}
}

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

   @JvmOverloadspublic 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);}@JvmOverloadspublic final void helloStudent(@NotNull String name) {helloStudent$default(this, name, 0, 2, (Object)null);}@JvmOverloadspublic 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 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\u0018\u0000 \u00032\u00020\u0001:\u0001\u0003B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0004"},d2 = {"LHello;", "", "()V", "Companion", "KotlinDemo"}
)
public final class Hello {@NotNullprivate 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 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0005\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\t\u001a\u00020\nR\u001a\u0010\u0003\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\b¨\u0006\u000b"},d2 = {"LHello$Companion;", "", "()V", "name", "", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "say", "", "KotlinDemo"})public static final class Companion {@NotNullpublic 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 methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}
}

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


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


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


Kotlin 代码 :

class Hello {companion object {@JvmStaticvar name = "Tom"@JvmStaticfun 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 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\u0018\u0000 \u00032\u00020\u0001:\u0001\u0003B\u0005¢\u0006\u0002\u0010\u0002¨\u0006\u0004"},d2 = {"LHello;", "", "()V", "Companion", "KotlinDemo"}
)
public final class Hello {@NotNullprivate static String name = "Tom";public static final Hello.Companion Companion = new Hello.Companion((DefaultConstructorMarker)null);@NotNullpublic static final String getName() {Hello.Companion var10000 = Companion;return name;}public static final void setName(@NotNull String var0) {Hello.Companion var10000 = Companion;name = var0;}@JvmStaticpublic static final void say() {Companion.say();}@Metadata(mv = {1, 1, 16},bv = {1, 0, 3},k = 1,d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0006\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\b\u0010\n\u001a\u00020\u000bH\u0007R$\u0010\u0003\u001a\u00020\u00048\u0006@\u0006X\u0087\u000e¢\u0006\u0014\n\u0000\u0012\u0004\b\u0005\u0010\u0002\u001a\u0004\b\u0006\u0010\u0007\"\u0004\b\b\u0010\t¨\u0006\f"},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@JvmStaticpublic static void name$annotations() {}@NotNullpublic final String getName() {return Hello.name;}public final void setName(@NotNull String var1) {Intrinsics.checkParameterIsNotNull(var1, "<set-?>");Hello.name = var1;}@JvmStaticpublic final void say() {String var1 = "Hello World";boolean var2 = false;System.out.println(var1);}private Companion() {}// $FF: synthetic methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}
}

Kotlin 类编译 时 , 自动生成了

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

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

public final class Hello {@NotNullprivate static String name = "Tom";@NotNullpublic static final String getName() {Hello.Companion var10000 = Companion;return name;}public static final void setName(@NotNull String var0) {Hello.Companion var10000 = Companion;name = var0;}@JvmStaticpublic static final void say() {Companion.say();}
}

http://www.ppmy.cn/news/31971.html

相关文章

YOLOv7(目标检测)入门教程详解---检测,推理,训练

目录 一.前言 二.yolov7源码下载 三.detect&#xff08;检测&#xff09; 四.Train&#xff08;训练&#xff09; 数据准备&#xff1a; labellmg: 配置训练的相关文件 配置数据集文件 正式训练&#xff1a; 推理&#xff1a; 推理效果&#xff1a; 五.总结 一.前言 …

《Redis实战篇》六、秒杀优化

6、秒杀优化 6.0 压力测试 目的&#xff1a;测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具&#xff1a;https://www.sqlfather.com/ &#xff0c;导入以下配置即可一键生成模拟数据 {"dbName":"hmdp",…

【SpringBoot3.0源码】启动流程源码解析 •下

文章目录初始化DefaultBootstrapContext开启Headless模式获取监听器并启动封装命令行参数准备环境打印Banner创建上下文容器预初始化上下文容器刷新Spring容器打印启动时间发布事件执行特定的run方法上一篇《【SpringBoot3.0源码】启动流程源码解析 • 上》&#xff0c;主要讲解…

【HashMap】| 深度剥析Java SE 源码合集Ⅱ | 你会吗?

目录一. &#x1f981; HashMap介绍1.1 特点1.2 底层实现二. &#x1f981; 结构以及对应方法分析2.1 结构组成2.1.1 成员变量2.1.2 存储元素的节点类型2.1.2.1 链表Node类2.1.2.2 树节点类2.1.2.3 继承关系2.2 方法实现2.2.1 HashMap的数组初始化2.2.2 计算hash值2.2.3 添加元…

华为OD机试真题2023(JAVA)

华为机试题库已由2022版换为2023版 华为机试有三道题目&#xff0c;第一道和第二道属于简单或中等题&#xff0c;分值为100分&#xff0c;第三道为中等或困难题&#xff0c;分值为200分。总分为400分&#xff0c;150分钟考试时间。之前通过为150分&#xff0c;现在好像分数提高…

【Java 语法篇】Java 六类运算符详解

算数运算符关系运算符逻辑运算符赋值运算符字符串连接运算符条件运算符在 Java 语言中&#xff0c;运算符有算数运算符、关系运算符、逻辑运算符、赋值运算符、字符串连接运算符、条件运算符。 算数运算符 算数运算符是我们最常用的运算符&#xff0c;用于完成基本的算术运算…

【手撕源码】vue2.x中keep-alive源码解析

&#x1f431; 个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff1a;vue3从入门…

Pytorch实现EdgeCNN(基于PyTorch实现)

文章目录前言一、导入相关库二、加载Cora数据集三、定义EdgeCNN网络3.1 定义EdgeConv层3.1.1 特征拼接3.1.2 max聚合3.1.3 特征映射3.1.4 EdgeConv层3.2 定义EdgeCNN网络四、定义模型五、模型训练六、模型验证七、结果完整代码前言 大家好&#xff0c;我是阿光。 本专栏整理了…