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 中变为public
。internal
类的成员经过名称修改,以防止在 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
操作。理想情况下,客户端代码不能通过Encrypt
或Decrypt
接口看到任何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)
AND 在init
块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 内部类公开可见的主要内容,如果未能解决你的问题,请参考以下文章