Kotlin for Android Developers: Visibility Modifiers
What is a visibility modifier?
A visibility modifier 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.
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 ofmain
); - 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 familiarised 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 haveprotected
visibility. In Java the visibility is according to the modifier and the default is stillpublic
.
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.
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
- Wikipedia: Access modifiers
- Kotlin Visibility Modifiers
- Java Access Modifiers
- Kotlin’s default visibility should be internal
- Another call for package private visibility in Kotlin
- Fragmented Podcast: Learning Kotlin — visibility modifiers