Внутренние классы Kotlin в Java видны публично
Я занимаюсь разработкой криптографической библиотеки Android в Котлине. У меня есть пара internal
классы, которые становятся общедоступными в приложении Java. Нашел это в документации.
internal
декларации становятсяpublic
на Яве. Членыinternal
классы проходят через искажение имен, чтобы затруднить их случайное использование из Java и разрешить перегрузку для членов с одинаковой подписью, которые не видят друг друга в соответствии с правилами Kotlin;
Есть ли способ обойти это?
4 ответа
Я видел, что все ваши внутренние классы все о шифровании и дешифровании.
Вы можете сделать это легко, определив функцию верхнего уровня и пометив ее как @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)
}
Тем не менее, это решило вашу проблему, но я все же хочу сказать, что ваш код может разбиться на мелкие кусочки, как дальше. например, алгоритм шифрования и дешифрования имеет много дубликатов, может быть, вы можете применить шаблонный метод шаблона в вашей библиотеке шифрования и представить интерфейсы, чтобы сделать вашу библиотеку явным образом и скрыть Cipher
операции по классам реализации. В идеале код клиента не может видеть java.security.*
занятия через Encrypt
или же Decrypt
интерфейсы. например:
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)
}
И плохо, что вы создаете экземпляр и вычисляете результат в init
блок здесь.
И вы можете использовать Factory Method Pattern, чтобы избежать проверки типов как в классах ECryptSymmetricDecrypt, так и в классах ECryptSymmetricEncrypt.
Помимо @JvmSynthetic
, ты можешь использовать @JvmName
с недопустимым идентификатором Java, например добавление пробела.
В качестве примера я добавил пробел в @JvmName
param, поэтому любые языки, кроме Kotlin, не смогут вызывать ваш метод:
@JvmName(" example")
internal fun example() {
}
Согласно моему ответу на этот вопрос в другом потоке:
Не идеальное решение, но я нашел два хакерских решения
Аннотируйте каждый публичный метод этого internal class
от @JvmName
с пробелами или специальными символами, которые будут генерировать синтаксическую ошибку в Java.
Например,
internal class LibClass {
@JvmName(" ") // Blank Space will generate error in Java
fun foo() {}
@JvmName(" $#") // These characters will cause error in Java
fun bar() {}
}
Поскольку это решение, приведенное выше, не подходит для управления огромным проектом или не кажется хорошей практикой, приведенное ниже решение может помочь.
Аннотируйте каждый публичный метод этого internal class
от @JvmSynthetic
по которым общедоступные методы недоступны для Java.
Например,
internal class LibClass {
@JvmSynthetic
fun foo() {}
@JvmSynthetic
fun bar() {}
}
Заметка:
Это решение защищает методы / поля функции. Согласно вопросу, он не скрывает видимость класса в Java. Так что идеальное решение этой проблемы все еще ожидается.
Использование частного конструктора + сопутствующего объекта, содержащего метод для создания экземпляра, аннотированного с помощью 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
}
}