# Property Accessors

To read a property, use its name. To assign a value to a mutable property, use the assignment operator =.

This reads and writes the property i:

// PropertyAccessors/Data.kt
package propertyaccessors
import atomictest.eq

class Data(var i: Int)

fun main() {
  val data = Data(10)
  data.i eq 10 // Read the 'i' property
  data.i = 20  // Write to the 'i' property

This appears to be straightforward access to the piece of storage named i. However, Kotlin calls functions to perform the read and write operations. As you expect, the default behavior of those functions reads and writes the data stored in i. In this atom you’ll learn to write your own property accessors to customize the reading and writing actions.

The accessor used to get the value of a property is called a getter. You create a getter by defining get() immediately after the property definition. The accessor used to modify a mutable property is called a setter. You create a setter by defining set() immediately after the property definition.

The property accessors defined in the following example imitate the default implementations generated by Kotlin. We display additional information so you can see that the property accessors are indeed called during reads and writes. We indent get() and set() to visually associate them with the property, but the actual association happens because get() and set() are defined immediately after that property (Kotlin doesn’t care about the indentation):

// PropertyAccessors/Default.kt
package propertyaccessors
import atomictest.*

class Default {
  var i: Int = 0
    get() {
      return field       // [1]
    set(value) {
      field = value      // [2]

fun main() {
  val d = Default()
  d.i = 2
  trace eq """

The definition order for get() and set() is unimportant. You can define get() without defining set(), and vice-versa.

The default behavior for a property returns its stored value from a getter and modifies it with a setter—the actions of [1] and [2]. Inside the getter and setter, the stored value is manipulated indirectly using the field keyword, which is only accessible within these two functions.

This next example uses the default implementation of the getter and adds a setter to trace changes to the property n:

// PropertyAccessors/LogChanges.kt
package propertyaccessors
import atomictest.*

class LogChanges {
  var n: Int = 0
    set(value) {
      trace("$field becomes $value")
      field = value

fun main() {
  val lc = LogChanges()
  lc.n eq 0
  lc.n = 2
  lc.n eq 2
  trace eq "0 becomes 2"

If you define a property as private, both accessors become private. You can also make the setter private and the getter public. Then you can read the property outside the class, but only change its value inside the class:

// PropertyAccessors/Counter.kt
package propertyaccessors
import atomictest.eq

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

fun main() {
  val counter = Counter()
  repeat(10) {
  counter.value eq 10

Using private set, we control the value property so it can only be incremented by one.

Normal properties store their data in a field. You can also create a property that doesn’t have a field:

// PropertyAccessors/Hamsters.kt
package propertyaccessors
import atomictest.eq

class Hamster(val name: String)

class Cage(private val maxCapacity: Int) {
  private val hamsters =
  val capacity: Int
    get() = maxCapacity - hamsters.size
  val full: Boolean
    get() = hamsters.size == maxCapacity
  fun put(hamster: Hamster): Boolean =
    if (full)
    else {
      hamsters += hamster
  fun take(): Hamster =

fun main() {
  val cage = Cage(2)
  cage.full eq false
  cage.capacity eq 2
  cage.put(Hamster("Alice")) eq true
  cage.put(Hamster("Bob")) eq true
  cage.full eq true
  cage.capacity eq 0
  cage.put(Hamster("Charlie")) eq false
  cage.capacity eq 1

The properties capacity and full contain no underlying state—they are computed at the time of each access. Both capacity and full are similar to functions, and you can define them as such:

// PropertyAccessors/Hamsters2.kt
package propertyaccessors

class Cage2(private val maxCapacity: Int) {
  private val hamsters =
  fun capacity(): Int =
    maxCapacity - hamsters.size
  fun isFull(): Boolean =
    hamsters.size == maxCapacity

In this case, using properties improves readability because capacity and fullness are properties of the cage. However, don’t just convert all your functions to properties—first, see how they read.

  • -

The Kotlin style guide prefers properties over functions when the value is cheap to calculate and the property returns the same result for each invocation as long as the object state hasn’t changed.

Property accessors provide a kind of protection for properties. Many object-oriented languages rely on making a physical field private to control access to that property. With property accessors you can add code to control or modify that access, while allowing anyone to use a property.

Exercises and solutions can be found at www.AtomicKotlin.com.