Question

Kotlin Public Getter Private Setter for var: How Accessors Work

Question

In Kotlin, how do you correctly declare a var so that its getter is public, but its setter is private and can only be modified from inside the class?

For example, the goal is to allow other code to read the property, while preventing external code from assigning a new value to it.

Short Answer

By the end of this page, you will understand how Kotlin property accessors work, how to make a var publicly readable but privately writable, and when this pattern is useful in real applications. You will also see the exact syntax, step-by-step behavior, common mistakes, and a small practical project.

Concept

In Kotlin, a property like var name = "Alice" usually creates:

  • a getter to read the value
  • a setter to change the value

By default, both accessors have the same visibility as the property itself.

A common design need is this:

  • other classes should be able to read the property
  • only the owning class should be able to change it

Kotlin supports this directly by letting you change the visibility of the setter.

var count: Int = 0
    private set

This means:

  • count can be read from anywhere the property itself is visible
  • count can only be assigned from inside the class where it is declared

This matters because it helps you protect object state. Instead of letting any code change important values freely, you control updates through methods or internal logic. This is a core part of encapsulation.

For example, a class might expose a score, status, or balance that others can inspect, but only the class itself should decide how those values change.

In short:

  • use val when a property should never change after initialization
  • use var with private set when it should change, but only internally

Mental Model

Think of a property as a room with two doors:

  • the getter door lets people look inside
  • the setter door lets people go in and rearrange things

A normal var leaves both doors open.

A var with private set works like this:

  • anyone allowed into the building can look through the getter door
  • only the room owner has the key to the setter door

So other parts of the program can see the current state, but they cannot directly change it.

That gives the class control over its own data.

Take Quiz

Syntax and Examples

Basic syntax

class User {
    var name: String = "Guest"
        private set
}

Here:

  • name is a mutable property
  • the getter is public by default
  • the setter is private

External code can do this:

val user = User()
println(user.name)

But this will fail:

user.name = "Sam" // Error: setter is private

Internal modification example

class Counter {
    var count: Int = 0
        private set

    fun increment() {
        count++
    }

    fun reset() {
        count = 0
    }
}

Usage:

val counter = Counter()
println(counter.count) // 0

counter.increment()
println(counter.count) // 1

counter.reset()
println(counter.count) // 0

This is beginner-friendly and very common in Kotlin classes.

With a custom getter and private setter

class Product(priceInput: Double) {
    var price: Double = priceInput
        private set
        get() = field
}

Usually you do not need to write a custom getter unless you want special behavior. private set alone is enough in most cases.

Step by Step Execution

Consider this code:

class BankAccount {
    var balance: Int = 0
        private set

    fun deposit(amount: Int) {
        if (amount > 0) {
            balance += amount
        }
    }
}

fun main() {
    val account = BankAccount()
    println(account.balance)
    account.deposit(50)
    println(account.balance)
}

Step by step:

  1. A BankAccount object is created.
  2. balance starts at 0.
  3. println(account.balance) calls the public getter and prints 0.
  4. account.deposit(50) calls a class method.
  5. Inside deposit, the class is allowed to use the private setter because it is inside the same class.
  6. balance += amount changes balance from 0 to 50.
  7. println(account.balance) calls the getter again and prints 50.

Important detail:

If you tried this in main:

account.balance = 100

it would not compile, because the setter is private.

Real World Use Cases

This pattern appears often in real Kotlin programs.

1. Counters and metrics

class DownloadTracker {
    var completedDownloads = 0
        private set

    fun markCompleted() {
        completedDownloads++
    }
}

Other code can read the count, but only the class can update it correctly.

2. State objects

class Connection {
    var isConnected = false
        private set

    fun connect() {
        isConnected = true
    }

    fun disconnect() {
        isConnected = false
    }
}

This prevents random code from setting connection state incorrectly.

3. Validation-based updates

class Temperature {
    var celsius: Double = 0.0
        private set

    fun setTemperature(value: Double) {
        if (value >= -273.15) {
            celsius = value
        }
    }
}

The class keeps control over valid values.

4. UI or app state

A view model or controller may expose a property for display, but only internal logic should change it.

class ScreenState {
    var loading = false
        private set

    fun startLoading() {
        loading = true
    }

    fun stopLoading() {
        loading = false
    }
}
Take Quiz

Real Codebase Usage

In real codebases, private set is often used as part of defensive design.

Common patterns

Encapsulation

Expose state for reading, but force changes through methods.

class Order {
    var status: String = "PENDING"
        private set

    fun ship() {
        status = "SHIPPED"
    }
}

This avoids invalid assignments like setting status to arbitrary strings from outside.

Guard clauses before updating

class ScoreBoard {
    var score = 0
        private set

    fun addPoints(points: Int) {
        if (points <= 0) return
        score += points
    }
}

The class validates data before changing state.

Error prevention

class Inventory {
    var stock = 0
        private set

    fun addItems(amount: Int) {
        require(amount > 0) { "Amount must be positive" }
        stock += amount
    }
}

This ensures every update follows the rules.

Read-only public API with mutable internal state

This is a common library design pattern:

  • public readers
  • controlled state changes
  • fewer unexpected bugs

It makes classes easier to reason about and safer to use.

Common Mistakes

1. Using val when internal mutation is needed

Broken idea:

class Counter {
    val count = 0

    fun increment() {
        // count++ // Error: val cannot be reassigned
    }
}

Use var with private set if the class needs to update the property.

class Counter {
    var count = 0
        private set

    fun increment() {
        count++
    }
}

2. Assuming private set makes the whole property private

class Example {
    var value = 10
        private set
}

This does not make value fully private. It only makes assignment private. Reading is still public if the property is public.

3. Writing the setter visibility on the same line

Incorrect:

class Example {
    private set var value = 10
}

Correct:

class Example {
    var value = 10
        private set
}

4. Expecting external code to assign to the property

val example = Example()
// example.value = 20 // Compile error

If outside code must change it, then the setter should not be private, or you should provide a method that performs the update.

5. Forgetting that methods inside the class can still change it

private set does not make the property immutable. It only restricts who can call the setter.

6. Overusing custom accessors

You usually do not need to write:

var x = 1
    get() = field
    private set(value) {
        field = value
    }

If you just want a private setter, use the shorter form:

var x = 1
    private set

Comparisons

ConceptCan read from outside?Can write from outside?Can write inside class?Best use
val x = ...YesNoNoTruly read-only property
var x = ...YesYesYesFully mutable property
var x = ... private setYesNoYesPublicly readable, internally controlled state
private var x = ...NoNoYesFully hidden internal property

val vs var private set

  • Use val if the value should never change after initialization.
  • Use var with private set if the class must update the value later.

private var vs var private set

  • private var hides both reading and writing from outside.
  • var private set still allows outside code to read the value.

Property setter vs update method

ApproachExampleWhen useful
Public settervar age = 0Simple mutable data
Private settervar age = 0; private setControlled public state
Method-based updatefun setAge(value: Int)Validation or business rules needed
Take Quiz

Cheat Sheet

Quick syntax

class Example {
    var value: Int = 0
        private set
}

What it means

  • getter: public by default
  • setter: private
  • outside code can read
  • outside code cannot assign
  • class methods can still assign

Example

class Counter {
    var count = 0
        private set

    fun increment() {
        count++
    }
}

Rules

  • private set must be placed under the property declaration
  • it only changes setter visibility
  • the property must be a var, not a val
  • val has no setter, so private set does not apply

Good use cases

  • counters
  • status flags
  • validated state
  • domain objects with controlled updates

Common choice guide

  • never changes: val
  • changes from anywhere: var
  • changes only inside class: var + private set

Edge case

You can also use other visibilities such as protected set or internal set when needed.

FAQ

How do I make a Kotlin property readable publicly but writable only inside the class?

Use a var and place private set under it.

var count = 0
    private set

Can I use private set with val in Kotlin?

No. A val does not have a setter, so there is nothing to make private.

Is the getter still public when I use private set?

Yes, if the property itself is public. Only the setter becomes private.

Can methods inside the same class change a property with private set?

Yes. The class that owns the property can still update it normally.

What is the difference between private var and var private set in Kotlin?

private var hides the property completely from outside code. var private set allows reading but blocks outside writing.

Why use private set instead of a normal public var?

It protects object state and ensures updates happen through controlled logic, such as validation methods.

Can I use internal set or protected set instead of private set?

Yes. Kotlin allows different setter visibility levels depending on how widely you want to allow writes.

Take Quiz

Mini Project

Description

Build a simple Player class for a game. Other parts of the program should be able to read the player's level, but only the class itself should be allowed to change it. This demonstrates how to protect state while still exposing useful information.

Goal

Create a class with a publicly readable level property and a privately writable setter, then update the level through class methods.

Requirements

  • Create a Player class with a level property.
  • Make the level property publicly readable but privately writable.
  • Add a method to increase the level.
  • Add a method that prints player information.
  • Show in main that reading works and direct assignment does not.
Take Quiz

Keep learning

Related questions

View more