Внутренние классы Kotlin в Java видны публично

Я разрабатываю криптографическую библиотеку для Android в Kotlin. У меня есть пара классов internal, которые становятся общедоступными в приложении Java. Нашел это в документации.

internal объявления становятся public в Java. Члены классов internal подвергаются искажению имен, чтобы затруднить случайное использование их из Java и разрешить перегрузку для членов с одинаковой сигнатурой, которые не видят друг друга в соответствии с правилами Kotlin;

Есть ли способ обойти это?


person priyank    schedule 29.07.2017    source источник
comment
вы можете использовать частную видимость. видимость типов верхнего уровня — это видимость package в Java.   -  person holi-java    schedule 29.07.2017
comment
Чтобы сохранить читабельность кода, я не могу сделать их приватными.   -  person priyank    schedule 29.07.2017
comment
Что ж. нет способа предотвратить использование клиентским кодом ваших внутренних классов Kotlin. @PublishedApi поддерживает только предупреждения, но клиентский код может заставить вызывать ваши внутренние классы.   -  person holi-java    schedule 29.07.2017
comment
@holi-java Это очень плохо. Наверное придется пожертвовать модульностью и сделать их private   -  person priyank    schedule 29.07.2017
comment
поэтому вы должны перепроектировать свой API. проблема в вашем дизайне, а не в видимости. возможно, многие функции могут быть частными, а некоторые функции доступны только во внутренних модулях.   -  person holi-java    schedule 29.07.2017
comment
Что ж, модули internal предназначены только для внутреннего использования, что отлично работает в приложении Kotlin. Там нечего выставлять. Проблема заключается в совместимости от Kotlin до Java, а не в дизайне, который я считаю.   -  person priyank    schedule 29.07.2017
comment
вы должны подумать, почему эта функция должна быть доступна в модуле. возможно, это может быть связано с видимостью пакета. Я не очень хорошо владею английским языком. так что я хочу, чтобы вы могли понять это.   -  person holi-java    schedule 29.07.2017
comment
Я понимаю, что сделать его private - это путь, но проблема в том, что класс очень большой, и создание его внутренним class еще больше снизит читаемость class.   -  person priyank    schedule 29.07.2017
comment
@ user25 Я не верю этой статье. Его примеры также недействительны. Например, ошибку смарт-приведения можно решить, просто добавив as Int.   -  person priyank    schedule 29.07.2017
comment
Я постараюсь ответить на ваш вопрос о том, как перепроектировать ваш код. Вы не возражаете?   -  person holi-java    schedule 29.07.2017
comment
Конечно, это — это класс.   -  person priyank    schedule 29.07.2017
comment
Почему вы хотите предотвратить публичную видимость этих классов в Java? Скомпрометирует ли это безопасность вашей библиотеки при вызове из Java, или вы просто не хотите давать никаких гарантий совместимости для них?   -  person Ilya    schedule 30.07.2017
comment
@Илья они там только для внутреннего пользования и не имеют цели разоблачения   -  person priyank    schedule 02.08.2017
comment
Любое решение???   -  person vihkat    schedule 15.05.2018
comment
@vihkat Теперь я использую internal object с @JvmSynthetic internal fun, что делает свое дело. Проверьте здесь github.com/ryan652/EasyCrypt/blob/master/easycrypt/src/main/   -  person priyank    schedule 15.05.2018
comment
Большое спасибо!   -  person vihkat    schedule 16.05.2018


Ответы (4)


Я видел все ваши внутренние классы относятся к зашифровать и расшифровать.

вы можете легко сделать это, определив функцию верхнего уровня и пометив ее как @JvmSynthetic, а затем создав ECryptSymmetricDecrypt и ECryptSymmetricEncrypt в private, чтобы предотвратить доступ клиента 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, чтобы избежать проверки типов как в ECryptSymmetricDecrypt и ECryptSymmetricEncrypt.

person holi-java    schedule 29.07.2017
comment
Использование вашего предложения внутренней функции верхнего уровня приводит к ошибке, говорящей о том, что internal функция предоставляет свой private тип возвращаемого значения ECryptSymmetricEncrypt. - person priyank; 30.07.2017
comment
@Ryan, вы определяете функцию верхнего уровня в связанных классах? - person holi-java; 30.07.2017
comment
@ Райан ох. Я знаю почему. вам не следует возвращать ECryptSymmetricEncrypt в функции верхнего уровня encrypt, так как она частная. вы должны сделать тип возвращаемого значения функции верхнего уровня равным Unit/ Any. - person holi-java; 30.07.2017
comment
Да, это решает. Теперь, как мне вызвать метод из другого класса? - person priyank; 30.07.2017
comment
@Ryan, вы должны применить то же правило, что и encrypt функции верхнего уровня. то в коде клиента Kotlin вы можете вместо этого использовать функцию верхнего уровня encrypt. - person holi-java; 30.07.2017
comment
Давайте продолжим обсуждение в чате. - person priyank; 30.07.2017

Помимо @JvmSynthetic, вы можете использовать @JvmName с недопустимым идентификатором Java, например, добавляя пробел.

В качестве примера я добавил пробел в параметре @JvmName, поэтому любые языки, кроме Kotlin, не смогут вызывать ваш метод:

@JvmName(" example")
internal fun example() {
}
person ice1000    schedule 13.11.2017
comment
это кажется таким ужасным, но умным и технически решает проблему, за которую проголосовали;) - person Chelsea Urquhart; 13.11.2018
comment
@ChelseaUrquhart Ура :-) - person ice1000; 14.11.2018
comment
@ice1000 Гений!! - person Ardy Febriansyah; 20.12.2020

Согласно моему ответу на этот вопрос в другом потоке:

Не идеальное решение, но я нашел два хакерских решения

Аннотируйте каждый общедоступный метод этого 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. Таким образом, идеальное решение этой проблемы все еще ожидается.

person Shreyas Patil    schedule 17.07.2020

Использование частного конструктора + сопутствующего объекта, содержащего метод для создания экземпляра, аннотированного с помощью 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
    }
}
person 05nelsonm    schedule 12.12.2020