Skip to content
AgileLogics
4 min read

Visibility Modifiers

Prerequisites: Classes and objects

Visibility modifiers control which code can see a class, property, or function. Hiding implementation details lets you change them later without breaking other code — this is encapsulation.

The four levels

Modifier Visible to
public Everyone — the default
internal Anything in the same module (one Gradle module, for example)
protected The class and its subclasses
private Inside the class only (or, at the top level, inside the file)

Example

class PasswordValidator(private val minimumLength: Int = 8) {
    fun isStrong(password: String) = isLongEnough(password) && hasDigit(password)

    private fun isLongEnough(password: String) = password.length >= minimumLength

    private fun hasDigit(password: String) = password.any { it.isDigit() }
}

fun main() {
    val validator = PasswordValidator()
    println(validator.isStrong("abc12345"))
    println(validator.isStrong("password"))
}
true
false

isStrong is the class's interface. The private helpers can be renamed or rewritten freely — nothing outside can call them. Trying validator.hasDigit("x") fails to compile: Cannot access 'fun hasDigit(password: String): Boolean': it is private in 'PasswordValidator'.

Constructor properties can be private too — private val minimumLength is stored in the object but hidden from outside.

Read-only from outside

A common pattern exposes a read-only view of mutable internal state:

class Playlist {
    private val _songs = mutableListOf<String>()
    val songs: List<String> get() = _songs

    fun add(song: String) {
        _songs += song
    }
}

fun main() {
    val p = Playlist()
    p.add("Intro")
    println(p.songs)
}
[Intro]

Outside code sees a read-only List and can't add songs directly.

In apps and libraries

In an app, public (the default) and private cover most needs. In a library, mark helpers internal or private, since everything public is part of what you promise to keep working.