1. Introduction
The Kotlin programming language is built upon the Java Virtual Machine (JVM). As such, it has to follow all of the rules that the JVM imposes – including visibility modifiers.
However, there are some subtle nuances in how the language implements these modifiers with regards to the compiler and the way our code is structured. This article will show some of the similarities and differences between Java and Kotlin in this regard.
2. Visibility Modifiers
Visibility modifiers are used to determine what other elements of code have access to the element being modified. They apply to some different elements in the code, at various levels of scope. The way these rules are applied may vary slightly between these different uses, which can be confusing at first.
2.1. Public Visibility
The most obvious modifier is public. This is possibly the most frequently utilized in the entire language and means that there are no additional restrictions on who can see the element being modified.
Unlike Java, in Kotlin there is almost never any need to declare anything as public – it is the default modifier used if we don’t declare another one. Other than this, it works the same in Kotlin as it does in Java.
If we apply the public modifier to a top-level element – an outer class or a function or variable declared directly inside a package – then any other code can access it. If we apply the public modifier to a nested element – an inner class, or a function or variable inside a class – then any code that can access the container can also access this element.
For example:
class Public {
val i = 1
fun doSomething() {
}
}
Class Public is accessible from anywhere in the entire codebase, “val i” and “fun doSomething()” are accessible from anything that can access Public.
2.2. Private Visibility
The other modifier that is used the majority of the time is private. This has almost the exact opposite meaning of public – it means that nobody can access it.
In reality, in Kotlin it means that only code declared inside the same scope can access it. This is subtly different from Java simply because Kotlin allows for multiple top-level declarations in the same file – a private top-level element can be accessed by everything else in the same file. Other than that the rules are the same. For example:
For example:
private class Private {
private val i = 1
private val doSomething() {
}
}
Class Private is only accessible from within the same source file, “val i” and “fun doSomething()” are only accessible from inside of class Private.
2.3. Protected Visibility
*The *p**rotected modifier in Kotlin means that it is strictly accessible only by the declaring class and subclasses**. This is the same as most people expect Java to work, but subtly different from how Java works.
In Java, the protected modifier also allows access to the element from anything else in the same package. For example, given the following class file:
package one;
class A {
protected val i = 1
}
The following file would work fine in Kotlin:
package two;
class B : A() {
fun getValue() : Int {
return i
}
}
The following file would work in Java but will not work in Kotlin:
package one;
class C {
fun getValue() : Int {
val a = A()
return a.i
}
}
And the following would not work in either Java or Kotlin:
package two;
class D {
fun getValue() : Int {
val a = A()
return a.i
}
}
Additionally, in Kotlin protected becomes the default visibility when we are overriding a protected element from a superclass. This can be explicitly changed to public if desired and is the main time we’ll need to declare something as public explicitly.
2.4. Package-Private / Default Visibility
In Java, there is an access modifier known as “package-private” (also referred to as “default”). This is used when no modifier is placed on the element. This means that any code in the same package can access it, but any code in a different package can not, including subclasses.
Kotlin does not currently have any support for this modifier at all, though this might change in the future. The reasoning for this is that it offers no protection – anyone could define code in the same package and access our elements, even from a different jar file.
2.5. Internal Visibility
Kotlin does add a new modifier to the options that Java does not currently support – internal. This modifier means that any code declared in the same module that is not otherwise restricted can access this element. (A module is essentially a Jar file.)
This is possible in Java using things like OSGi, but it’s not native to the language at present. Java 9 will be bringing some concepts that make it more achievable by being able to export public identifiers selectively.
This adds a huge benefit for writing APIs and implementations. We can write our API interfaces as public, our main implementation as public classes, and all of the support code that it depends on as internal. Doing this means that external code is forced to go through the API and can not get access to internals. For example:
package com.baeldung.modifiers
internal class Internal {
}
class Public {
internal val i = 1
internal fun doSomething() {
}
}
Class Internal is only accessible from inside the same module. “val i” and “fun doSomething()” are also only accessible from inside the same module, even though the class they are inside can be accessed from anywhere.
3. Summary
In the article, we had a look at visibility modifiers in Kotlin.
Most of the time, the visibility modifier rules in Kotlin work the same as we’d expect from Java. However, there is a major difference – which is the introduction of the internal scope – which is very useful for larger projects.