@JvmName 是 Kotlin 中的一个注解(Annotation),主要用于自定义 Kotlin 声明(类、函数、属性等)在 JVM 平台上的名称。
它的核心作用是解决 Kotlin 与 Java 互操作时因命名规则差异导致的兼容性问题,让 Kotlin 代码在 Java 中调用时更灵活、更符合 Java 开发习惯。
核心作用
Kotlin 的某些语法特性(如顶层函数、扩展函数、伴生对象方法等)在编译为 JVM 字节码时,默认的命名规则可能与 Java 的预期不一致。
例如:
• Kotlin 的顶层函数会被编译到名为 FileNameKt 的类中(如 Utils.kt 生成 UtilsKt 类),Java 中需通过 UtilsKt.method() 调用。
• Kotlin 的扩展函数在 JVM 中会生成一个静态方法,但方法名可能包含接收者类型信息(如 String.extension() 可能生成 extension(String))。
@JvmName 允许开发者显式指定这些声明在 JVM 中的名称,避免默认的“不友好”命名,提升跨语言调用的可读性。
使用场景与示例
标注顶层函数/属性(Top-level Declarations)
Kotlin 的顶层函数(不在任何类或对象中定义的函数)会被编译到一个与文件名相关的类中(默认规则:文件名 + "Kt")。使用 @JvmName 可以自定义这个类名。
示例:
// 文件:StringUtils.kt
@file:JvmName("StringHelper") // 自定义 JVM 类名为 StringHelper(替代默认的 StringUtilsKt)
package com.example.utils
fun isEmpty(str: String): Boolean = str.isEmpty()
// 调用(Kotlin):isEmpty("hello")
// 调用(Java,默认):StringUtilsKt.isEmpty("hello") → 不直观
// 调用(Java,使用 @JvmName):StringHelper.isEmpty("hello") → 更清晰
说明:
@file:JvmName 需写在文件顶部(package 语句之前),作用于整个文件的顶层声明。value 参数为目标 JVM 类名(无需包名,包名由 package 语句决定)。
标注扩展函数/属性(Extension Declarations)
Kotlin 的扩展函数在 JVM 中会生成一个静态方法,方法名默认格式为 扩展函数名(接收者类型)(如 String?.isNullOrEmpty() 生成 isNullOrEmpty(String))。使用 @JvmName 可以自定义方法名。
示例:
// 扩展函数:为 String? 添加判空扩展
@JvmName("isStringNullOrEmpty") // 自定义 JVM 方法名为 isStringNullOrEmpty
fun String?.isNullOrEmpty(): Boolean = this == null || this.isEmpty()
// 调用(Kotlin):str.isNullOrEmpty()
// 调用(Java,默认):StringUtilsKt.isNullOrEmpty(str) → 方法名不明确
// 调用(Java,使用 @JvmName):StringUtilsKt.isStringNullOrEmpty(str) → 语义更清晰
说明:
扩展函数的 @JvmName 需标注在函数上,value 参数为目标 JVM 方法名(需符合 Java 方法命名规范)。
标注伴生对象(Companion Object)的方法/属性
伴生对象的成员默认在 JVM 中通过 Companion 接口访问(如 MyClass.Companion.create())。结合 @JvmStatic 和 @JvmName,可以进一步自定义方法名。
示例:
class MyClass {
companion object {
// 默认:Java 中需通过 MyClass.Companion.newInstance()
fun create(): MyClass = MyClass()
// 使用 @JvmStatic + @JvmName:Java 中可直接通过 MyClass.newObj()
@JvmStatic
@JvmName("newObj")
fun createInstance(): MyClass = MyClass()
}
}
// 调用(Java):
// MyClass.Companion.create() → 原始方式
// MyClass.newObj() → 自定义名称和静态访问
说明:
@JvmName 可与 @JvmStatic 配合使用,同时控制方法的 JVM 名称(静态化)和访问方式。
标注类/接口的别名(Type Aliases)
Kotlin 的类型别名(typealias)在 JVM 中默认不直接暴露,但结合 @JvmName 可以为类型别名指定一个“虚拟”的 JVM 类名(通常用于文档或反射场景)。
示例:
@JvmName("UserIdAlias")
typealias UserId = Long
// 反射时可通过 "UserIdAlias" 识别该类型别名(实际仍是 Long)
注意事项
• 避免名称冲突:自定义的 JVM 名称不能与同一包下的其他类/方法重名,否则会导致编译错误。
• 与 @JvmMultifileClass 配合:若多个 Kotlin 文件使用相同的 @file:JvmName,需添加 @file:JvmMultifileClass 注解,将这些文件的顶层声明合并到同一个 JVM 类中(适用于工具类拆分)。
// File1.kt
@file:JvmName("StringUtils")
@file:JvmMultifileClass
package com.example.utils
fun func1() {}
// File2.kt
@file:JvmName("StringUtils")
@file:JvmMultifileClass
package com.example.utils
fun func2() {}
// Java 中可统一通过 StringUtils.func1() 和 StringUtils.func2() 调用
• 注解位置限制:@JvmName 的位置需严格遵循语法规则(如 @file:JvmName 必须在文件顶部,@JvmName 标注函数/属性时需直接作用于声明)。
总结
@JvmName 是 Kotlin 与 JVM 平台交互的重要工具,通过自定义声明在 JVM 上的名称,解决了 Kotlin 语法特性(如顶层函数、扩展函数)在 Java 中调用的兼容性问题。合理使用 @JvmName 可以显著提升跨语言代码的可读性和可维护性。