Public get private set in Kotlin: pitfalls and solutions

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Public get, private set in Kotlin: how to avoid the pitfalls (and what to do if you've already stepped in them)

\n\n

Hello, colleague! If you've ever Googled "Kotlin public get private set var", you are not alone. This query is a real hit on Stack Overflow, and thousands of developers (including myself at one time) have banged their heads on the keyboard trying to understand why their code doesn't work.

\n\n

It seems simple, right? I want the property to be accessible to everyone (public get), but only the class itself can change it (private set). In Java, this was routine: declare a private field, write a getter and setter, and make the setter private. In Kotlin, everything should be simpler, but...

\n\n

Spoiler: the syntax exists, but it doesn't always work the way you expect. In this article, we'll break down the top mistakes that even experienced developers make and show you how to avoid them. Let's go!

\n\n

Anatomy of a property: var, val, get, and set

\n\n

First, a quick primer. In Kotlin, properties are not just fields. They are full-fledged elements that can contain getters and setters. Here is the basic structure:

\n\n
var propertyName: Type = default_value\n    get() = field\n    set(value) { field = value }
\n\n

If you use var, Kotlin automatically generates a getter and setter. But you can override their behavior. You can also change their visibility (visibility modifiers: public, private, internal, protected).

\n\n

And this is where it gets interesting. To make the getter public and the setter private, you need to write:

\n\n
var name: String = "default"\n    private set
\n\n

That's it? Almost. But there are nuances that break this mechanism.

\n\n

Mistake #1: Forgetting about the constructor

\n\n

The most common problem is trying to declare a public get private set var in the class constructor. For example:

\n\n
// ❌ Error: this is not allowed\nclass User(public get private set var name: String)
\n\n

Kotlin will throw a compilation error. Why? Because constructor parameters are not properties. They are just parameters. If you want a property with a modified setter visibility, you need to declare it in the class body.

\n\n

How to do it correctly:

\n\n
class User(name: String) {\n    var name: String = name\n        private set\n}
\n\n

Or, if you are using a data class, you'll have to work around it:

\n\n
data class User(private val _name: String) {\n    var name: String = _name\n        private set\n}
\n\n

Yes, data class does not allow you to directly change the setter visibility for properties from the primary constructor. You'll have to use a backing field.

\n\n

Mistake #2: Confusion with the backing field

\n\n

A backing field is a hidden field that Kotlin automatically generates for a property if you use field in the getter or setter. But if you override the getter or setter without referencing field, the backing field is not created.

\n\n

Example of the problem:

\n\n
var counter: Int = 0\n    private set\n    get() = field * 2 // ❌ Error: field is not defined
\n\n

No, this is not a compilation error. But if you write:

\n\n
var counter: Int = 0\n    private set\n    get() = 42 // it's fine, but field is not used
\n\n

In this case, the backing field will not be created. And if you try to reference field in the setter, you'll get an error. The good news: if you use private set, the backing field is almost always created automatically, because the default setter uses field. But if you override the setter without field — be prepared for surprises.

\n\n

Tip: always explicitly use field in the getter and setter if you want to work with the backing field. And remember: if a property does not have a backing fie

Blogs

Book Recommendations