如何在Java中调用Kotlin定义的Value class及相关实例创建方法
问题原因
Kotlin的@JvmInline value class(原内联类)为了实现无额外性能损耗的类型包装,在JVM字节码层面会做特殊处理:
- 所有接收或返回value class类型的Kotlin函数,默认会触发名称混淆(name mangling),函数名会被自动加上特殊后缀,Java侧无法识别到原始名称的函数,所以会出现
buildCountry()方法不存在的现象。你可以正常调用buildString()是因为它返回普通String类型,不会触发混淆逻辑。 - 该混淆机制和是否添加
@JvmStatic无关,只要返回值是value class就会触发,所以加@JvmStatic没有效果。 - 同时value class的构造函数默认私有,是Kotlin为了保证内联类实例只能按规则生成,避免Java直接构造破坏内联逻辑。
解决方法
方案1:添加@JvmName显式指定Java侧可见的函数名(最推荐)
该方案可以保留value class的内联性能优势,同时兼容Java调用,只需要给返回value class的函数添加注解即可:
class IdentifierFactory { companion object { fun buildString(): String { return "hello" } // 显式指定Java侧可见的方法名 @JvmName("buildCountry") fun buildCountry(): CountryId { return CountryId.empty() } // 配合@JvmStatic使用也可以 @JvmStatic @JvmName("buildCountryStatic") fun buildCountryStatic(): CountryId = CountryId.empty() } }
修改后Java侧即可正常调用:
// 调用Companion方法 CountryId countryId = IdentifierFactory.Companion.buildCountry(); // 调用静态方法 CountryId countryId2 = IdentifierFactory.buildCountryStatic();
方案2:改为普通data class(无性能要求时可用)
如果你的场景对性能没有极致要求,只是需要类型安全的包装,可以直接把value class改成普通data class,完全不需要额外适配即可兼容Java调用:
data class CountryId(private val id: UUID) { override fun toString(): String = id.toString() companion object { fun empty(): CountryId = CountryId(EMPTY_UUID) } }
方案3:基础类型中转(仅特殊场景使用)
如果Java侧只需要拿到CountryId对应的原始值,也可以在工厂类新增返回UUID类型的方法,Java侧拿到底层值后需要转CountryId时再到Kotlin侧处理,该方案适配成本较高,无特殊需求不推荐。
内容的提问来源于stack exchange,提问作者CaffGeek
相关产品推荐
相关产品推荐

