Внутренние классы 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
    }
}
Другие вопросы по тегам