Kotlin for Android Developers: Visibility Modifiers

What is a visibility modifier?

A visibility modifier (a.k.a access modifiers or access specifiers) is a concept not tied to any specific programming language. It is a language keyword that precedes a declaration and defines the scope from where it is visible -thus can be accessed. Visibility modifiers facilitate encapsulation.

There are four visibility modifiers in Kotlin: private, protected, internal and public. The default visibility modifier is public.

Local variables, functions, and classes cannot have visibility modifiers in Kotlin.

Private

The declarations marked with the private modifier are visible inside the file/class containing the declaration.

Protected

The declarations marked with the protected modifier are visible inside the file containing the declaration and subclasses. This modifier is not allowed for top-level declarations.

Internal

The declarations marked with the internal modifier are visible everywhere in the same module.

A module is a set of Kotlin files compiled together:

  • an IntelliJ IDEA module;
  • a Maven project;
  • a Gradle source set (with the exception that the test source set can access the internal declarations of main);
  • a set of files compiled with one invocation of the <kotlinc> Ant task.

Public

The declarations marked with the public modifier are visible to everyone. public is the default visibility modifier in Kotlin (a declaration without explicit modifier will be public)

Please notice that in the previous snippets the public word is unnecessary. Writing public is no different to avoid the visibility modifier given that public is the default visibility modifier in Kotlin

Constructors visibility

The primary constructor of a class is public by default. This effectively means that its visibility is determined by the visibility of the class — the constructor can never have looser visibility than the class itself.

We can explicitly declare a visibility modifier for the primary constructor:

Overriding protected methods

When overriding a protected method, the default visibility of the overridden method is still protected, not public.

The fact that the lack of visibility modifier changes its meaning when overriding a protected method is not obvious.

Access modifiers (or access specifiers) are keywords in object-oriented languages that set the accessibility of classes, methods, and other members. Access modifiers are a specific part of programming language syntax used to facilitate the encapsulation of components. — Wikipedia

Java vs Kotlin

If you’re not familiarise with access modifiers in Java, you can skip this section. I’m not going to explain in details how access modifiers work in Java, I’ll focus on the main differences between Java modifiers and Kotlin modifiers so is easier to understand by people coming from Java.

Kotlin modifiers work in a similar way to Java’s, but with some nuances:

  • In Java, the default modifier is package private, in Kotlin is public.
  • Java’s package private doesn’t have an equivalent in Kotlin, the closest is internal.
  • Classes and Interfaces can be private in Kotlin.
  • An Outer class does not see private members of its inner class in Kotlin.
  • In Kotlin if you override a protected member and do not specify the visibility explicitly, the overriding member will also have protected visibility. In Java the visibility is according to the modifier and the default is still public.

What does it mean for Android

Visibility modifiers are not exactly new in Android. Kotlin has twisted them a little bit but in general, everything remains the same. The aim of modifiers is facilitating the encapsulation of components and the same good practices apply when using them in Kotlin.

Given that modifiers in Kotlin are slightly different to Java, I’d like to mention the things I’ve come across so far.

  • public becomes rare

The fact that internal is a slightly restricted version of public reduces a lot the usage of the public modifier. public is only desirable when you want visibility across modules. In case your Android project consists in just one app module, then public is useless.

In the scenarios when you do really need public, (you need visibility across modules) you don’t need to type it because is the default (with the exception mentioned above when making public a overridden protected).

public it’s likely to be the less common modifier in a Kotlin code base and typing public is very rare.

  • Packages become less relevant

Packages are are a big deal in Java. The default visibility is package private, and half of the modifiers involve the idea of packages. This has a great impact on the packaging and structure of Java projects.

In Kotlin packages are not even considered when dealing with modifiers. No modifier takes into consideration the concept of a package.

In addition, given the conciseness and short syntax of Kotlin, top-level declarations in the same file become common.

This is better in a single file than in four files

For readability, the previous snippet it’s better in a single file than in four different files. That was not always the case in Java given the verbose nature of the language.

Files alleviate to a certain degree the need of package trees (common in Java) making the project structure flatter and less cluttered.

  • protected becomes more relevant

In Kotlin protected doesn’t involve packages. It’s purely the current class/interface and and sub classes. This makes protected much more intuitive and more desirable to use. Encapsulation and inheritance get along with protected better with Kotlin in my opinion.

  • internal is everywhere

Package private doesn’t exist in Kotlin. public is (as stated above) rare. So, what do we use when private and protected are not enough? internal

In Android projects with a single module, then internal should be extremely common. With one single module, visibility across modules is out of the question, so public is useless. And given that internal is not the default visibility modifier, you do need to type it for everything that is not private or protected.

Hey, wait a minute… what’s the difference between public and internal in a project with just one app module?

None.

So given that public is the default and I don’t need to type it, why shouldn’t I just use public and avoid spreading internal everywhere?

That’s an approach I wouldn’t recommend. First, as a rule of thumb for good encapsulation, you should try always to use the more restrictive modifier possible. Second, in case you start adding more modules to the project, internal vs public makes a big difference and will mess with encapsulation badly.

  • internal can be extremely useful

When dealing with multi module projects in Android, internal is an extremely convenient modifier. It makes things visible everywhere in the current module, but not visible outside. public vs internal in a module defines the frontier between the module’s API and the implementation details. This is specially important for library developers, where that frontier needs to be very well defined.

We stated before that in Kotlin packages become less relevant, files more relevant and hierarchies flatter. In modern Android projects, the number of modules is increasing rapidly and they are usually not very big. The sum of all of it makes internal the modifier that I most commonly type.

  • Default visibility should be internal (!?)

If you’ve made it so far, you might be wondering Why in the world is not internal the default modifier? You are not alone.

I don’t know about other platforms, but when it comes to Android, I still don’t know what are the advantages of having public instead of internal as the default modifier. I’m not saying is wrong, I’m just saying is unknown to me.

I can’t explain it better than what it’s exposed in this thread, so I’ll leave it here.

Summary

Kotlin does not reinvent the wheel with visibility modifiers. They are very similar to any other programming language that uses them and just slightly different from Java.

Visibility modifiers are used to facilitate encapsulation, and that should be kept in mind at all times. Declarations with looser visibility than necessary is usually considered a bad usage of modifiers. Visibility should be as restricted as possible and as open as necessary.

The adoption of public as the default visibility for Kotlin was a controversial topic, but it was pretty much put to rest a few years ago.

References

Kotlin for Android Developers series