Kotlin internal classes in Java visible publicly

Viewed 6129

I am developing an Android crypto library in Kotlin. I have a couple of internal classes which become publicly visible in a Java app. Found this in documentations.

internal declarations become public in Java. Members of internal classes go through name mangling, to make it harder to accidentally use them from Java and to allow overloading for members with the same signature that don't see each other according to Kotlin rules;

Is there a way to get around this?

4 Answers

Apart from @JvmSynthetic, you can use @JvmName with an illegal Java identifier, like adding a space.

As an example, I added a space in the @JvmName param, so any languages except Kotlin will not be able to invoke your method:

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

As per my answer on this question in another thread:

Not perfect solution but I found two hacky solutions

Annotate every public method of that internal class by @JvmName with blank spaces or special symbols by which it'll generate syntax error in Java.

For e.g.

internal class LibClass {

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

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

Since this above solution isn't appropriate for managing huge project or not seems good practice, this below solution might help.

Annotate every public method of that internal class by @JvmSynthetic by which public methods aren't accessible by Java.

For e.g.

internal class LibClass {

    @JvmSynthetic
    fun foo() {}

    @JvmSynthetic
    fun bar() {}
}

Note:

This solution protects the methods/fields of the function. As per the question, it does not hide the visibility of class in Java. So the perfect solution to this is still awaited.

Utilizing a private constructor + companion object containing method to instantiate annotated with JvmSynthetic preserves encapsulation.

// 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
    }
}
Related