111

In Java, we have the package protected (default) modifier for classes, which allows us to have many classes in a single package but exposes only a few and keeps the logic encapsulated.

With Kotlin this doesn't seem to be the case. If I want a few classes to be visible to each other but no further, I have to use a private modifier which limits visibility to a single file.

So if you want 10 classes in a package but only one of them to be public, you'd have to have one huge file with all the classes in it (and private all over the place).

Is this normal practice or there is a way to achieve some similar modularity in Kotlin?

I don't understand: if they have the notion of a package, why did they get rid of package protected access?

Update: We might have package protected visibility after all
see the discussion here

Update: If you read through the discussion and still think this is a must-have feature for the language, please vote here

medavox
  • 175
  • 2
  • 11
vach
  • 10,571
  • 12
  • 68
  • 106

5 Answers5

69

Kotlin, compared to Java, seems to rely on packages model to a lesser degree (e.g. directories structure is not bound to packages). Instead, Kotlin offers internal visibility, which is designed for modular project architecture. Using it, you can encapsulate a part of your code inside a separate module.

So, on top level declarations you can use

  • private to restrict visibility to the file
  • internal to restrict visibility to the module

At this point, there is no other option for visibility restriction.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 11
    Well i like the internal, but we will have that fixed much better in java9 however i never ever saw that someone would create the same package just to use code... after all if someone is that much determined to violate the modularity he will just copy paste the code if necessary... What i'm concerned here is usability. cos simply putting ton of code in one file is dumb... – vach Mar 10 '16 at 14:37
  • 1
    @vach I actually saw - and did - that. Some libraries are frustratingly ignorant to your use cases (doesn't mean it's good practice) – Lovis Mar 10 '16 at 16:32
  • 8
    Ok but right now i'm migrating a java project to kotlin, and i have very good visibility enforced, tipicaly these are some utilities that are composed of 4-5 classes only one of which is visible to the outside world... Now with kotlin i have no option to have the same visibility unless i put everything in the same file... Do i understand it correctly or there is some other approach i dont know of? – vach Mar 10 '16 at 18:25
  • @vach, well, in Kotlin it's OK to put a few classes with similar meaning or interacting with each other inside one file. IMHO, for 4-5 classes this would be OK. When there's more classes, Kotlin modules system encourages you to move them into a separate lib module, which is quite easy if you use Maven/Gradle. – hotkey Mar 10 '16 at 18:47
  • 6
    Only each additional module = increased compilation time; there's no way you would want to convert your packages one-to-one to modules. The idea to put 4-5 classes into a single file is also quite controversial, I think. – AndroidEx Mar 11 '16 at 02:16
  • Putting 4-5 classes in a single file is quite common in Kotlin, not controversial. For small classes it happens a lot, along with functions. – Jayson Minard Mar 11 '16 at 14:00
  • 38
    @JaysonMinard I agree about small classes. But let's say your Java package consists of 5 classes, 200 lines each, and suddenly to mimic the same visibility in Kotlin you have to create a 1000 lines long file. That's what I was calling controversial above. – AndroidEx Mar 11 '16 at 23:31
  • 9
    Package level visibility is just cool in Java. Even it can be "hacked" you don't have to do it. It makes unit testing of protected stuff easy and well separated. "protected" visibility in Java also increases readability, it says you mustn't use the member directly, unless you are the package maintainer. Breaking down a project into more projects/modules just for the right visibility issues just doesn't make sense, just annoying extra work. So in this issue Java is still better than Kotlin. – Endre May 27 '17 at 14:24
  • 1
    After using kotlin for a while, i do think kotlin does it better, their internal makes more sense... basically for all the java devs, if you think there is something wrong with kotlin just use it for some time and you'll probably come to same conclusions as the architects did :) – vach Jun 25 '17 at 03:45
  • 29
    "the main flaw of Java's package protected visibility is that everyone can still access your code" - a rather poor reason if you ask me. You can use reflection to access anything you want anyway. Access modifiers are not for security, they are for preventing accidental mistakes and specifying API contracts. If someone writes the code inside the same package, it's safe to assume that this isn't an accident and he must know what he is doing. If you want security, use `SecurityManager`. – Malcolm Sep 17 '17 at 12:26
  • @Malcolm Thanks for pointing that out. I agree, and with Jigsaw prohibiting split packages, this problem ceases to exist. I should edit the answer and remove that point, but anyway Kotlin does not rely on packages, maybe for some other reason. – hotkey Sep 17 '17 at 13:47
  • For everyone who thinks this is essential, please upvote and comment in [this YouTrack request](https://youtrack.jetbrains.com/issue/KT-29227) – Mister Smith Jun 02 '20 at 11:00
  • 1
    since i have used kotlin for more than 6 years now i thought i'd add how this went... Like many i ended up working around it buy putting bunch of stuff that would belong in a package protected scope into single file... do i like it no, mostly i hate their stubbornness of not implementing it despite clearly lot of people needing it. My assumption is that they envision kotlin as language beyond jvm hence package protected visibility being very specific to jvm did not belong... either way it sucks but its not a deal breaker and i still find it valuable to code in kotlin – vach May 11 '21 at 06:37
  • 1
    and no i never used internal as solution to this... although i tried, it is slow and clumsy when you break down things aggressively into modules, it is good for folks who write libs – vach May 11 '21 at 06:38
  • 1
    @vach I am with you. Personally, i get very angry whenever i try to use module system. It is too compilcated and sometimes buggy. And this makes consistent project structure management hard. To make just one word of internal keyword work properly, users who were friendly with package-private visibility and only-root-module project structure have to do a huge amount of refactoring. Jetbrains must think again since if they don't establish easy-to-use project management/build system, they may fail to success with Kotlin/JS and Kotlin/Native. –  Aug 16 '21 at 13:44
  • 1
    I tried to use it only once and it was obvious to me its never going to be used by majority... everyone gonna do huge kotlin files, which to be frank you can see them doing in standard library, good thing is kotlin is open source, shall things like this accumulate there will be incentive for people do implement this and just keep original repo in sync with community version. – vach Aug 17 '21 at 14:11
9

As a workaround for me on android I've created @PackagePrivate annotation and lint checks to control access. Here you can find the project.

Lint checks are obviously not that strict as compiler checks and some setup needed to fail the build on errors. But android studio picks up lint checks automatically and shows error immediately while typing. Unfortunately I don't know a way to exclude annotated members from autocomplete.

Also, as lint is a purely compile-time tool, no checks at runtime performed.

esentsov
  • 6,372
  • 21
  • 28
  • is it actually specific for android? kotlin is not just android it would be awesome to have libs generic when possible – vach Jan 14 '20 at 05:36
  • Unfortunately it's android specific and the reason is is uses Android Lint. Android Studio supports it nicely and runs the checks on the fly, showing errors in the IDE itself immediately. It's probably possible to implement these checks with something like `ktlint`, but without IDE support user experience is really bad for such kind of checks. – esentsov Jan 14 '20 at 06:33
  • 1
    Hint: You can make it more like a keyword and avoid having to import it on every file by adding type aliases on each package, for example: `internal typealias pkg = io.github.esentsov.PackagePrivate; internal typealias private = io.github.esentsov.FilePrivate`. And use it like `@pkg val ... @private internal val ...` – geekley Apr 16 '20 at 01:32
  • U should look into the @hide annotation of android for excluding in autocomplete. – Zakir Sheikh Dec 25 '21 at 19:46
  • I'd gladly include something like this in my project even without IDE support. Are you aware of a way to do this? I use IDEA, which is a superset of Android studio. It's strange why you need to include android packages :( – Lutosław Feb 04 '22 at 12:29
7

A near-replacement for package private visibility is available using the opt-in requirements feature (credit to pdvrieze on Kotlin discussions). This is the annotation syntax typically used to flag experimental features in an API.

To use it, create an annotation denoting package private declarations:

@RequiresOptIn(message = "Only to be used in MyPackage")
@Retention(AnnotationRetention.BINARY)
annotation class MyPackagePrivate

Then annotate any methods you want to be package private with it:

@MyPackagePrivate
fun aPackagePrivateMethod() {
   // do something private within a package
}

In this way a warning will be generated on any method that calls the annotated method unless the calling method is itself annotated with the corresponding @OptIn annotation, here shown at class level:

@OptIn(MyPackagePrivate::class)
class AClassInThePackage {
   fun userOfPackagePrivateMethod() {
      aPackagePrivateMethod()
   }
}

This, then, produces a similar effect to Java's package private, except that calling methods need to explicitly opt in to using a package private declaration.

If it is desired to generate an error rather than a warning, the level parameter of @RequiresOptIn can be specified:

@RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Only to be used in MyPackage")
// annotation declaration as before

Simon Jacobs
  • 1,147
  • 7
  • 7
6

As @hotkeys points out, you can use the internal keyword in a module or you can put all classes that would otherwise belong in a package inside a single file, but sticking several classes in a file may be a questionable design decision.

For me, the package visibility is helpful for its documenting value. I want to know what public interface some package is presenting to the rest of the project, hide factory implementation classes and so on.

So even if it's possible to access package-private classes and methods in Java, I still choose to use the package modifier.

For this I created a project with a single annotation:

package com.mycompany.libraries.kotlinannotations;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(SOURCE)
@Target({ TYPE, METHOD, CONSTRUCTOR })
/**
 * Use in Kotlin code for documentation purposes. 
 * 
 * Whenever a Kotlin class or method is intended to be accesible at package level only.
 *
 */
public @interface PackagePrivate {

}

Then I can use this annotation in any Kotlin project.

The second step, which I haven't done yet, is creating a PMD rule to enforce this with maven (or any other build tool for that matter) and also be able to see violations of the rule in my IDE with the pmd plugin.

There no is full Kotlin support in pmd at this moment but it seems to be expected at some point.

DPM
  • 1,960
  • 3
  • 26
  • 49
-11

Package-based protection is pointless in Kotlin because packages themselves are unprotected

In Java, package was tied to directory structure. So if you put your classes in com\example\yoursecretengine, any attempt (deliberate or accidental) to add a rogue class there would be easily noticeable. This is the kind of security we've depended on.

Kotlin removes the ties between directory and package, so I can put my class in "my" directory (eg.src\java\pl\agent_l\illegalaccess) yet declare its package as com.example.yoursecretengine - and gain access to all the properties you've meant as package private.

In fact, a Kotlin project works perfectly without ANY package declarations. This only highlights that packages are "more what you'd call guidelines than actual rules". They're a convenience feature, useful only to unclutter namespace and nothing more.

Relevant quotes from kotlinlang:

unlike many other languages, Kotlin packages do not require files to have any specific locations w.r.t. itself; the connection between a file and its package is established only via a package header.

And:

an absence of a package header in a file means it belongs to the special root package.

Agent_L
  • 4,960
  • 28
  • 30
  • please see [this](https://kotlinlang.org/spec/packages-and-imports.html#packages-and-imports) – mightyWOZ Aug 07 '21 at 09:12
  • @mightyWOZ Thanks, I've included your link. How do you like it now? – Agent_L Aug 07 '21 at 13:32
  • 7
    no one uses package visibility for any sort of security im tired of this argument, its about packaging your code in a nice way, it sucks putting related stuff in one file, as much as kotlin is compact it sucks to do that its that simple – vach Aug 08 '21 at 10:02
  • 2
    they force you either put everything in one file or pollute public namespace if you are working on any non hello world project i bet you do one or the other – vach Aug 08 '21 at 10:04
  • 3
    @vach This argument is getting repeated because people keep repeating this error. Both Java and Kotlin are general-purpose languages, so if general populace misuses a feature - that feature has negative value overall. Kotlin forces us into modules which is IMHO a good direction even if it feels like using sledgehammer to crack a nut at first. Package private is a horrible hack that makes you feel your code is neat when it's not. So you either embrace spaghetti or actually partition it with modules. – Agent_L Aug 09 '21 at 06:22
  • 4
    doesn't matter, i have debated about this so much i'm tired of it, i have no idea how people used it, to me package visibility was a way to separate components into multiple classes and only have one of them exposed, because its not in kotlin i see everyone doing huge files in kotlin like its some javascript, it sucks but i found my ways around it – vach Aug 10 '21 at 08:33