前言
KCP的应用计划分两篇,本文是第一篇
本文主要记录从发现问题到使用KCP解决问题的折腾过程,下一篇记录KCP的应用
背景
Kotlin 号称百分百兼容 Java ,所以在 Kotlin 中一些修饰符,比如 internal ,在编译后放在纯 Java 的项目中使用(没有Kotlin环境),Java 仍然可以访问被 internal 修饰的类、方法、字段等
在使用 Kotlin 开发过程中需要对外提供 SDK 包,在 SDK 中有一些 API 不想被外部调用,并且已经添加了 internal 修饰,但是受限于上诉问题且第三方使用 SDK 的环境不可控(不能要求第三方必须使用Kotlin)
带着问题Google一番,查到以下几个解决方案:
以上方案可以满足大部分需求,但是以上方案都不满足隐藏构造方法,可能会想什么情景下需要隐藏构造方法,例如:
class Builder(internal val a: Int, internal val b: Int) {
/**
* non-public constructor for java
*/
internal constructor() : this(-1, -1)
}
为此我还提了个Issue3,期望官方把 JvmSynthetic 的作用域扩展到构造方法,不过官方好像没有打算实现😂
为解决隐藏构造方法,可以把构造方法私有化,对外暴露静态工厂方法:
class Builder private constructor (internal val a: Int, internal val b: Int) {
/**
* non-public constructor for java
*/
private constructor() : this(-1, -1)
companion object {
@JvmStatic
fun newBuilder(a: Int, b: Int) = Builder(a, b)
}
}
解决方案说完了,大家散了吧,散了吧~
开玩笑,开玩笑😛,必然要折腾一番
折腾
探索JvmSynthetic实现原理
先看下 JvmSynthetic 注解的注释文档
/**
* Sets `ACC_SYNTHETIC` flag on the annotated target in the Java bytecode.
*
* Synthetic targets become inaccessible for Java sources at compile time while still being accessible for Kotlin sources.
* Marking target as synthetic is a binary compatible change, already compiled Java code will be able to access such target.
*
* This annotation is intended for *rare cases* when API designer needs to hide Kotlin-specific target from Java API
* while keeping it a part of Kotlin API so the resulting API is idiomatic for both languages.
*/
好家伙,实现原理都说了:在 Java 字节码中的注解目标上设置 ACC_SYNTHETIC 标识
此处涉及 Java 字节码知识点,ACC_SYNTHETIC 标识可以简单理解是 Java 隐藏的,非公开的一种修饰符,可以修饰类、方法、字段等4
得看看 Kotlin 是如何设置 ACC_SYNTHETIC 标识的,打开 Github Kotlin 仓库,在仓库内搜索 JvmSynthetic 关键字 Search · JvmSynthetic (github.com)

在搜索结果中分析发现 JVM_SYNTHETIC_ANNOTATION_FQ_NAME 关联性较大,继续在仓库内搜索 JVM_SYNTHETIC_ANNOTATION_FQ_NAME 关键字 Search · JVM_SYNTHETIC_ANNOTATION_FQ_NAME (github.com)



在搜索结果中发现几个类名与代码生成相关,这里以 ClassCodegen.kt 为例,附上相关代码
// 获取Class的SynthAccessFlag
private fun IrClass.getSynthAccessFlag(languageVersionSettings: LanguageVersionSettings): Int {
// 如果有`JvmSynthetic`注解,返回`ACC_SYNTHETIC`标识
if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME))
return Opcodes.ACC_SYNTHETIC
if (origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION &&
languageVersionSettings.supportsFeature(LanguageFeature.SamWrapperClassesAreSynthetic)
)
return Opcodes.ACC_SYNTHETIC
return 0
}
// 计算字段的AccessFlag
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
origin.flags or visibility.flags or
(if (isDeprecatedCallable(context) ||
correspondingPropertySymbol?.owner

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



