Kotlin to support package protected visibility


From what I can see, the main problem with internal compare to package private is that internal is tied to Gradle/Maven modules. This discourages developers from building small modules, as doing so affects compilation time and complexity of the overall project structure. Package private solves those problems, but introduces a lot of problems by itself. Instead, I propose that we look for alternative solutions. My suggestion would be to add a notion of modules within the language itself, that are more lightweight than what the build systems offer, but also more flexible than packages, since packages are restricted to a single namespace and a single directory file structure.


The “lightweight” module I prefer is the ability to mark a package as a module. This restriction is only enforced at compile time. Those members marked with internal will then be compiled to package-private members post-fixed with ugly module name to tell Kotlin it’s internal.


How about defining a scope which is not visible either from outside the package or from outside the module?


The Dylan language allows you to expose multiple different “modules” (named lists of types, functions, etc.) from each “library” (compilation unit), so you could have things exposed for general use from one module, and things for testing from another. Modules would also import things from other modules. There are no doubt other languages which do similar things.

It works well in terms of encapsulation options, but it forces you to explicitly list everything you want to export in a module file, separate from the definition of the thing, which is a pain to maintain.

I could imagine Kotlin having a default module for exporting public things, and @ExportFrom annotations on other things to export them from one or more other modules. But how you would represent that cross-platform behind the scenes, I don’t know.


You have jigsaw for that. Kotlin does not fully support it yet, but it will.


I’d heard about jigsaw but not looked into it yet. It seems like a great thing but only operates at the class level. I don’t see an obvious way to export different fields or methods of a class via different modules, which some might want for testing. Perhaps one could make a class method package-scope, have it implement a package-scope interface from a sub-package, then export the sub-package from a separate module and/or export it only to test modules? Not sure that works, though.

Of course, you can usually refactor to avoid having to call some otherwise private/protected methods for testing.


Actually, java modules work at the package level, not the class level.


Sorry, I wasn’t clear. I meant that the finest possible level of granularity I could see as being possible would be the class level, if you had one class in a package. But I didn’t see a way to expose a class and only some of its methods from a package.


Custom scopes might be interesting. Like having an extension scope for plugins and a user scope for regular users. Perhaps they don’t need to be public either (internal, but explicit). One limitation would be the need to state the actual scope on the use side per file (or at build/gradle level)


So what’s the current stance on this from the Jetbrains team?


Companion object can access private constructor


Any roadmap or progress in this direction? I’d love to not have my project namespace polluted with dozens of classes that could be easily package isolated.


Feature request: https://youtrack.jetbrains.net/issue/KT-29227


private does not provide any real encapsulation. Any other module in the system can use reflection and get full access to its internals. Can this mean private can also be omitted?


No longer true for Java 9+ where it can be actually restricted. But you don’t write access specifiers to protect against people who want access at any cost, you do it to protect it against unintended access. Modifiers are not a security measure.


That’s actually also his point. He argues that the same can be replied to @yole, hidden in a somewhat sarcastic tone.