Start learning free

LESSON 185 OF 348 · OBJECT-ORIENTED PROGRAMMING

Access Modifiers in Kotlin: public, private and More

Lesson Read the full explanation

TL ; DR:

  • An access modifier controls who can see a member of a class. The two you'll use most are public (anyone) and private (only this class).


  • Make data private so callers can't break invariants. Expose what they need through public methods (often called getters and setters):

    class Dog(private val name: String) {
        fun getName(): String {
            return name
        }
    }
    

  • Outside the class, rex.name is a compile error. Use rex.getName() instead.



Full lesson:

So far every field we wrote was public. That's convenient, but leaks internals. If you later decide to validate or compute the value differently, every caller in the codebase breaks.


Encapsulation

Encapsulation is the idea that an object's data should be hidden behind methods. The class controls how it's read and written; outside code only sees the API.

class Account {
    private var balance: Double = 0.0

    fun deposit(amount: Double) {
        if (amount < 0) throw IllegalArgumentException()
        balance += amount
    }
}

The four Kotlin modifiers

publicVisible everywhere (the default).
privateVisible only inside the same class.
protectedVisible in the same class and its subclasses.
internalVisible everywhere in the same module.

For now, focus on public and private.


Your task

The Dog class's name property is now private. Add a fun getName(): String method that returns it.

Dog's name: Rex
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · OBJECT-ORIENTED PROGRAMMING

    Access Modifiers in Kotlin, explained

    Why hide anything

    So far every property was visible to all code. That is convenient, but it exposes the class's internals. If a later change needs validation, or a different way of computing a value, every piece of code that touched the property directly may break. Encapsulation is the answer: keep the data inside the class and let outside code reach it only through the class's functions. An access modifier says who may see a member.

    The four modifiers

    ModifierVisible to
    publicEveryone. This is the default and is usually left out.
    privateOnly code inside the same class.
    protectedThe class and its subclasses.
    internalAny code in the same module, which is one compiled unit of a project.

    For now, public and private are the two to focus on. A modifier goes in front of the declaration, and for a constructor property it goes before val or var:

    class Dog(private val name: String)
    

    Inside Dog, name is available as usual. Outside, it is not. Given val rex = Dog("Rex"), the expression rex.name fails to compile with an error saying that name is private.

    protected and top-level private

    A protected member can be used inside the class and inside its subclasses, but not by unrelated code. If Animal declares protected var energy = 10, a Dog that extends it can write energy -= 3, while Animal("Rex").energy from outside is a compile error. Inheritance itself comes in a later lesson.

    Modifiers also work on top-level declarations. A private function or class declared at the top of a file is visible only inside that file.

    Exposing data through functions

    A private member is not useless to outsiders; the class chooses what to offer. A private property plus a public function that returns it is the classic getter. A public function that changes it is the setter, and it can check the new value:

    class Account {
        private var balance: Double = 0.0
    
        fun deposit(amount: Double) {
            if (amount < 0) throw IllegalArgumentException("negative")
            balance += amount
        }
    
        fun getBalance(): Double = balance
    }
    

    Callers cannot set balance to a nonsense value. They can only call deposit, which rejects a negative amount with an IllegalArgumentException.

    The Kotlin shortcut

    Kotlin properties already come with accessors, so you often do not write getters by hand. To make a value readable but not writable from outside, keep it public and restrict the setter:

    class Counter {
        var count: Int = 0
            private set
    
        fun inc() {
            count++
        }
    }
    

    Outside code can read c.count but c.count = 5 does not compile. The class itself can still change it through inc.

    One trap: a public val name and a function getName() in the same class collide. The property generates a getter with that exact name on the JVM, and the compiler reports a platform declaration clash. In the lesson's code name is private, so it generates no getter and fun getName() is fine. In a new class, a public val name alone is more idiomatic.

    What private really means

    Private is per class, not per object. A function in Dog may read the private property of a different Dog. If Dog has fun same(other: Dog) = name == other.name, it compiles and compares two private names. Private also does not make a value secret at run time; it only controls what the compiler accepts.

    The exercise

    The starter code gives Dog a private name and calls rex.getName(), which does not exist yet. Step 1 declares a public function getName that returns a String. Step 2 makes it return the property, which is allowed because the function belongs to the class:

    class Dog(private val name: String) {
        fun getName(): String {
            return name
        }
    }
    
    class Solution {
        fun mainFunction() {
            val rex = Dog("Rex")
            println("Dog's name: " + rex.getName())
        }
    }
    

    The output is Dog's name: Rex.

    Common mistakes

    • Reading the private property directly. rex.name from Solution is a compile error. Go through getName().
    • Making the getter private too. Functions are public by default, so leave the modifier off.
    • Forgetting the return type. A block-body function without : String returns Unit, so return name does not compile.
    • Writing both a public property and a same-named getter. That is the platform clash described above.

    Key takeaways

    • Encapsulation hides data behind functions so a class can change its internals safely.
    • public is the default; private limits access to the class, protected adds subclasses, and internal opens it to the module.
    • A private property with a public getter function is the classic pattern; private set is the Kotlin shortcut.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free