0赞
赞赏
更多好文
上周在Code Review时,我看到同事在Java里调用Kotlin的单例方法,写了这么一串:
MyUtilsKt.getSingleton().doSomething();
我当时就笑了——"为啥不加@JvmStatic?" 他一脸懵,我只好掏出我的"Kotlin互操作性工具箱",告诉他这些注解能让你的Java代码干净得像Kotlin写的。
1. @JvmStatic:告别ClassNameKt的尴尬
场景:你写了个伴生对象工具类,Java调用时要写MyUtilsKt.INSTANCE.method(),太难看了。
解决:加@JvmStatic,Java直接调用静态方法:
object MyUtils {
@JvmStatic
fun doSomething() { /* ... */ }
}
Java调用:
MyUtils.doSomething(); // 不再需要MyUtilsKt.INSTANCE
💡 个人经验:我之前在项目里把所有伴生对象方法都加了
@JvmStatic,Java同事说"终于不用写INSTANCE了,这下舒服多了"。
2. @JvmField:别让getter/setter拖后腿
场景:你有个数据类,Java需要直接访问字段,但Kotlin默认生成getter/setter。
解决:用@JvmField直接暴露字段:
data class User(
@JvmField val name: String,
@JvmField var age: Int
)
Java调用:
User user = new User("Alice", 30);
System.out.println(user.name); // 直接访问字段,不是getName()
⚠️ 警告:别滥用!如果需要校验逻辑,别用
@JvmField。我曾经在数据类上乱加这个,结果导致Java直接修改了数据,引发了一个bug。
3. @file:JvmName:告别MyClassKt的丑陋命名
场景:Kotlin文件StringUtils.kt生成的类名是StringUtilsKt,Java调用时得写StringUtilsKt.trim(" abc ")。
解决:加@file:JvmName("StringUtils"):
@file:JvmName("StringUtils")
fun trim(str: String) = str.trim()
Java调用:
StringUtils.trim(" abc "); // 看起来像Java原生方法
💡 我的日常:在Kotlin文件开头,我第一件事就是加
@file:JvmName,让Java调用时"感觉像自己写的"。
4. @file:JvmMultifileClass:合并多个文件的顶级函数
场景:你有多个Kotlin文件,都定义了顶级函数,Java调用时需要分别导入。
解决:在所有文件中加@file:JvmMultifileClass,然后指定@file:JvmName:
// 文件1: StringUtils.kt
@file:JvmName("StringUtils")
@file:JvmMultifileClass
fun trim(str: String) = str.trim()
// 文件2: NumberUtils.kt
@file:JvmName("NumberUtils")
@file:JvmMultifileClass
fun toInt(str: String) = str.toInt()
Java调用:
StringUtils.trim(" abc ");
NumberUtils.toInt("123");
💡 实战经验:我们项目里有20+个工具类,用这个注解后,Java代码从
StringUtilsKt.trim()变成了StringUtils.trim(),同事说"终于像Java代码了"。
5. @Nullable & @NotNull:空安全的桥梁
场景:Kotlin的空安全和Java的默认可空类型不匹配。
解决:在Java方法上标注注解,Kotlin能自动识别:
// Java
@Nullable
public String getName() { ... }
@NotNull
public List<String> getItems() { ... }
Kotlin调用:
val name: String? = user.getName() // 自动推断为可空
val items: List<String> = user.getItems() // 自动推断为非空
💡 重要:这些注解必须在正确的位置(方法参数、返回值、字段),否则Kotlin无法正确推断空类型。我见过同事把注解写在类上,结果Kotlin还是把所有方法都当成可空。
6. @JvmOverloads:已经提过,但值得再强调
场景:Kotlin函数有默认参数,Java调用时需要全部传。
解决:加@JvmOverloads,Java自动生成重载方法:
@JvmOverloads
fun sendNotification(title: String, message: String = "Hello", priority: Int = 1) { ... }
Java调用:
NotificationUtil.sendNotification("Alert"); // 省略默认参数
💡 个人铁律:所有会被Java调用的Kotlin函数,如果有默认参数,必须加
@JvmOverloads。我已经看到太多同事因为忘记这个注解,导致Java代码写得又长又丑。
最后说句大实话
互操作性不是Kotlin的"高级特性",而是日常开发的基本功。我见过太多人写Kotlin时"只考虑Kotlin",结果Java同事写调用代码时疯狂抱怨。
下次写Kotlin时,问问自己:
- "这个函数会被Java调用吗?"
- "需要加
@JvmStatic吗?" - "参数有默认值吗?要加
@JvmOverloads"
别让Java同事觉得"Kotlin写得漂亮,Java调用却很丑"。互操作性注解就是让你的Kotlin代码在Java世界里也"体面"的工具。
(PS:别问我为什么叫"互操作性工具",反正能用就行 😄)
