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:
countcan be read from anywhere the property itself is visiblecountcan 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
valwhen a property should never change after initialization - use
varwithprivate setwhen 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.
Syntax and Examples
Basic syntax
class User {
var name: String = "Guest"
private set
}
Here:
nameis 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:
- A
BankAccountobject is created. balancestarts at0.println(account.balance)calls the public getter and prints0.account.deposit(50)calls a class method.- Inside
deposit, the class is allowed to use the private setter because it is inside the same class. balance += amountchangesbalancefrom0to50.println(account.balance)calls the getter again and prints50.
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
}
}
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
| Concept | Can read from outside? | Can write from outside? | Can write inside class? | Best use |
|---|---|---|---|---|
val x = ... | Yes | No | No | Truly read-only property |
var x = ... | Yes | Yes | Yes | Fully mutable property |
var x = ... private set | Yes | No | Yes | Publicly readable, internally controlled state |
private var x = ... | No | No | Yes | Fully hidden internal property |
val vs var private set
- Use
valif the value should never change after initialization. - Use
varwithprivate setif the class must update the value later.
private var vs var private set
private varhides both reading and writing from outside.var private setstill allows outside code to read the value.
Property setter vs update method
| Approach | Example | When useful |
|---|---|---|
| Public setter | var age = 0 | Simple mutable data |
| Private setter | var age = 0; private set | Controlled public state |
| Method-based update | fun setAge(value: Int) | Validation or business rules needed |
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 setmust be placed under the property declaration- it only changes setter visibility
- the property must be a
var, not aval valhas no setter, soprivate setdoes 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.
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
Playerclass with alevelproperty. - Make the
levelproperty publicly readable but privately writable. - Add a method to increase the level.
- Add a method that prints player information.
- Show in
mainthat reading works and direct assignment does not.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.