Java 中的 Kotlin 内部类公开可见

Posted

技术标签:

【中文标题】Java 中的 Kotlin 内部类公开可见【英文标题】:Kotlin internal classes in Java visible publicly 【发布时间】:2018-01-05 16:47:45 【问题描述】:

我正在使用 Kotlin 开发 android crypto library。我有几个 internal 类在 Java 应用程序中公开可见。在文档中找到this。

internal 声明在 Java 中变为 publicinternal 类的成员经过名称修改,以防止在 Java 中意外使用它们,并允许重载具有相同签名但根据 Kotlin 规则看不到彼此的成员;

有没有办法解决这个问题?

【问题讨论】:

你可以向下使用private可见性。***类型的可见性是 Java 中的 package 可见性。 为了保持代码的可读性,我不能将它们设为私有。 好吧。没有办法阻止客户端代码使用您的内部 Kotlin 类。 @PublishedApi 只支持警告,但是客户端代码可以强制调用你的内部类。 @holi-java 太糟糕了。可能将不得不牺牲模块化并使它们成为private @vihkat 我现在将internal object@JvmSynthetic internal fun 一起使用。在这里查看github.com/ryan652/EasyCrypt/blob/master/easycrypt/src/main/… 【参考方案1】:

我看到你所有的internal classes 都是关于encrypt 和decrypt。

您可以通过定义一个***函数并将其标记为@JvmSynthetic,然后将ECryptSymmetricDecrypt 和ECryptSymmetricEncrypt 类设置为私有 来防止Java 客户端访问您的内部类,例如:

// define this top-level function in your ECryptSymmetricEncrypt.kt

@JvmSynthetic internal fun <T> encrypt(
                                       input:T, password: String, cipher:Cihper, 
                                       erl: ECryptResultListener, outputFile:File,
                                       getKey:(String,ByteArray)->SecretKeySpec)

  ECryptSymmetricEncrypt(input, password, cipher,
                 pass, salt -> getKey(pass, salt) , erl, outputFile)

但是,它解决了您的问题,但我仍然想说您的代码可以进一步分解成小块。例如,加密和解密算法有很多重复,也许你可以在你的加密库中应用Template Method Pattern并引入接口来显式地使你的库和隐藏实现类下的Cipher操作。理想情况下,客户端代码不能通过EncryptDecrypt 接口看到任何java.security.* 类。例如:

interface Encrypt
   //          v--- don't include the infrastructure class here,e.g:`Keys`,`Cipher`
   fun encode(...args)


interface Decrypt
   //          v--- don't include the infrastructure class here,e.g:`Keys`,`Cipher`
   fun decode(...args)

ANDinit 块here 中创建实例并计算结果是一件坏事。

AND您可以使用Factory Method Pattern 来避免ECryptSymmetricDecrypt 和ECryptSymmetricEncrypt 类中的类型检查。

【讨论】:

使用您对***内部函数的建议会产生一个错误,提示 internal 函数暴露了其private 返回类型ECryptSymmetricEncrypt @Ryan 你在相关类中定义了顶层函数吗? @Ryan 哦。我知道为什么。您不应该在***函数encrypt 中返回ECryptSymmetricEncrypt,因为它是私有的。您应该将***函数的返回类型设置为Unit/Any 是的,解决了它。现在如何从另一个类调用该方法? @Ryan 您应该应用与encrypt ***函数相同的规则。然后在您的 Kotlin 客户端代码中,您可以使用***函数 encrypt【参考方案2】:

除了@JvmSynthetic,您还可以将@JvmName 与非法Java 标识符一起使用,例如添加空格。

例如,我在 @JvmName 参数中添加了一个空格,因此除 Kotlin 之外的任何语言都无法调用您的方法:

@JvmName(" example")
internal fun example() 

【讨论】:

这看起来很糟糕,但聪明且技术上解决了如此受欢迎的问题;) @ChelseaUrquhart 干杯 :-) @ice1000 天才!!【参考方案3】:

根据我在another 线程中对这个问题的回答:

不是完美的解决方案,但我找到了两个 hacky 解决方案

@JvmName 用空格或特殊符号注释internal class 的每个公共方法,这样会在Java 中产生语法错误。

例如

internal class LibClass 

    @JvmName(" ") // Blank Space will generate error in Java
    fun foo() 

    @JvmName(" $#") // These characters will cause error in Java
    fun bar() 

由于上述解决方案不适合管理大型项目或似乎不是好的做法,因此以下解决方案可能会有所帮助。

@JvmSynthetic 注释internal class 的每个公共方法,Java 无法通过这些方法访问公共方法。

例如

internal class LibClass 

    @JvmSynthetic
    fun foo() 

    @JvmSynthetic
    fun bar() 

注意:

此解决方案保护函数的方法/字段。根据问题,它不会隐藏 Java 中类的可见性。所以这个问题的完美解决方案还在等待中。

【讨论】:

【参考方案4】:

利用私有构造函数 + 包含方法的伴随对象来实例化用 JvmSynthetic 注释的方法保留封装。

// Private constructor to inhibit instantiation
internal class SomeInternalClass private constructor() 

    // Use the companion object for your JvmSynthetic method to
    // instantiate as it's not accessible from Java
    companion object 
        @JvmSynthetic
        fun instantiate(): SomeInternalClass =
            SomeInternalClass()
    

    // This is accessible from Java
    @JvmSynthetic
    internal var someVariable1 = false

    // This is accessible from Java
    @JvmSynthetic
    var someVariable2 = false



    // This is inaccessible, both variable and methods.
    private var someVariable3 = false
    @JvmSynthetic
    fun getSomeVariable3(): Boolean =
        someVariable3
    @JvmSynthetic
    fun setSomeVariable3(boolean: Boolean) 
        someVariable3 = boolean
    

【讨论】:

以上是关于Java 中的 Kotlin 内部类公开可见的主要内容,如果未能解决你的问题,请参考以下文章

[Kotlin] 内部类

Kotlin:为什么构造函数参数默认具有“内部”可见性?

Kotlin 中内部可见性修饰符的范围

对比Java学Kotlin可见性修饰符

对比Java学Kotlin可见性修饰符

对比Java学Kotlin可见性修饰符