2
votes

I am migrating a Java codebase to Kotlin, and I have a Java class with the following quite common structure:

class A {
    // public static factories
    // private constructors
    // public A methods
    // private A methods
    // private static helpers
}

My problem is what to do with the private static helpers.

In Kotlin, if I put them as file-level private functions:

class A private constructor(...) {
    companion object {
        // factories
    }
    // A methods
    // private A methods
}

// private helpers

Then when using the code from Java there will be a new AKt class in the package, with no methods but still visible (and appearing as suggestion in the IDE). What was nicely isolated before now "leaks" as a new unintended public class into the API.

Having the private static helpers in the companion object is not really the same, because now the "logical" organization of the class is all "backwards" with the private, unimportant helpers being at the top:

class A private constructor(...) {
    companion object {
        // factories
        // private helpers
    }
    // A methods
    // private A methods
}

Is there a better alternative? Some way to hide the AKt generated class maybe?


Edit: I though that the AKt class had a public constructor because IntelliJ doesn't mark this as an error when editing the file:

AKt a = new AKt();

Not in the same project, and not in a client.

But when building the project, or when compiled with Gradle, it does fails with:

error: cannot find symbol
AKt mk = new AKt();
3
You can also define private helpers as actual local functions, defining them within another function, if they're only used in one place. Or just stop treating them as static, and let them be instance functions!Louis Wasserman

3 Answers

1
votes

Make it an internal class. It will no longer be visible in the public API.

1
votes

Well, put them in a non-companion object:

// optional to make the methods available without `Helpers.`
import A.Helpers.*

class A private constructor(...) {
    companion object {
        // factories
    }
    // A methods
    // private A methods
    private object Helpers {
        // private helpers
    }
}
0
votes

You can use @JvmSynthetic annotation. This is what docs says:

Sets ACC_SYNTHETIC flag on the annotated target in the Java bytecode.

Synthetic targets become inaccessible for Java sources at compile time while still being accessible for Kotlin sources. Marking target as synthetic is a binary compatible change, already compiled Java code will be able to access such target.

This annotation is intended for rare cases when API designer needs to hide Kotlin-specific target from Java API

while keeping it a part of Kotlin API so the resulting API is idiomatic for both languages.