Kotlin-KCP的应用-第一篇

前言

KCP的应用计划分两篇,本文是第一篇

本文主要记录从发现问题到使用KCP解决问题的折腾过程,下一篇记录KCP的应用

背景

Kotlin 号称百分百兼容 Java ,所以在 Kotlin 中一些修饰符,比如 internal ,在编译后放在纯 Java 的项目中使用(没有Kotlin环境),Java 仍然可以访问被 internal 修饰的类、方法、字段等

在使用 Kotlin 开发过程中需要对外提供 SDK 包,在 SDK 中有一些 API 不想被外部调用,并且已经添加了 internal 修饰,但是受限于上诉问题且第三方使用 SDK 的环境不可控(不能要求第三方必须使用Kotlin)

带着问题Google一番,查到以下几个解决方案:

  1. 使用 JvmName 注解设置一个不符合 Java 命名规则的标识符1
  2. 使用 ˋˋKotlin 中把一个不合法的标识符强行合法化1
  3. 使用 JvmSynthetic 注解2

以上方案可以满足大部分需求,但是以上方案都不满足隐藏构造方法,可能会想什么情景下需要隐藏构造方法,例如:

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)

image-20220508120615028

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

image-20220508121250275

image-20220508121115651

image-20220508121137580

在搜索结果中发现几个类名与代码生成相关,这里以 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
Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.internal.jdk7.JDK7PlatformImplementations found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.internal.jdk7.JDK7PlatformImplementations$ReflectSdkVersion found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations$ReflectSdkVersion found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.io.path.ExperimentalPathApi found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.io.path.PathRelativizer found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.io.path.PathsKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.io.path.PathsKt__PathReadWriteKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.io.path.PathsKt__PathUtilsKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.jdk7.AutoCloseableKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk7-1.6.21.jar -> kotlin-stdlib-jdk7-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21) Duplicate class kotlin.jvm.jdk8.JvmRepeatableKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.random.jdk8.PlatformThreadLocalRandom found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.streams.jdk8.StreamsKt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$1 found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$2 found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$3 found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$4 found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.text.jdk8.RegexExtensionsJDK8Kt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Duplicate class kotlin.time.jdk8.DurationConversionsJDK8Kt found in modules kotlin-stdlib-1.8.10.jar -> kotlin-stdlib-1.8.10 (org.jetbrains.kotlin:kotlin-stdlib:1.8.10) and kotlin-stdlib-jdk8-1.6.21.jar -> kotlin-stdlib-jdk8-1.6.21 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21) Go to the documentation to learn how to Fix dependency resolution errors.
09-25
解决Kotlin依赖冲突(Duplicate class错误,涉及kotlin - stdlib - 1.8.10和kotlin - stdlib - jdk7 - 1.6.21、kotlin - stdlib - jdk8 - 1.6.21)可尝试以下方法: ### 明确冲突来源 通过执行指令 `./gradlew :app:dependencies > dependencies.txt` 查看依赖情况,打开生成的 `dependencies.txt` 文件搜索 "kotlin - stdlib" ,找出所有相关依赖版本,再搜索具体冲突版本(如 "kotlin - stdlib:1.8.10"),以明确是哪个依赖引入了冲突版本。例如在引用[2]中,通过此方法发现是facebook三方登录依赖变更导致冲突。 ### 指定依赖版本 如果发现是某个三方库依赖导致版本冲突,可将该三方库指定为固定版本,避免其自动更新依赖版本引发冲突。如引用[2]中将facebook依赖指定版本为16.2.0: ```groovy implementation 'com.facebook.android:facebook-login:16.2.0' ``` ### 使用依赖约束 在 `build.gradle(:app)` 的 `dependencies` 块中添加约束,强制使用指定版本的依赖。可参考引用[3]添加如下代码: ```groovy constraints { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.10") { because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") } implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") { because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") } } ``` ### 使用平台声明 在 `build.gradle(:app)` 的 `dependencies` 块中使用 `platform` 声明指定Kotlin版本,确保所有Kotlin依赖使用统一版本,如引用[4]: ```groovy dependencies { implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0")) // 其他依赖 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值