Mutable vals in Kotlin

When I first learned Kotlin, the difference between val and var seemed simple: val means immutable and var means mutable.

The truth is more nuanced than that: val does not mean immutable, val means read-only. That means that you're not allowed to explicitly write to a val, but it doesn't guarantee that they're immutable[1].

Mutable Class Properties

For variables, the distinction between immutable and read-only is a moot point. There's no way to write a val variable or override how it is retrieved, so it is (for all intents and purposes) immutable.

For class properties, however, the read-only nature of val makes a huge difference.

In the context of properties, val vs. var indicates whether getters/setters exist for the property. A var has both a getter and a setter, whereas a val only has a getter.

In the simple case, the lack of a setter means that val class properties are immutable. However, it is possible to add a custom getter function for any class property, allowing you to return whatever you want each time someone accesses the property. For example:

class Person(val birthDay: DateTime) {
  val age: Int
    get() = yearsBetween(birthDay, DateTime.now())
}

As you can see, there's no explicit way to set Person.age, but Person.age will change values as the current date changes.

In fact, thinking of Person.age as a variable at all is a misnomer. It's actually a getter function that you're calling that may change values over invocations.

Consequences

I was personally horrified when I learned about mutable vals in Kotlin. I felt betrayed - val vs. var marking data as immutable vs. mutable was one of the first cool features from Kotlin I learned!

As far as I can tell, there are two arguments to be made for customizable getters for val class properties:

  1. Class properties are just a shorthand for getters/setters, and customizing getters is generally thought of as okay.
  2. Customizable getters enable delegated properties.

Delegated properties are a compelling use case and I will continue to use them. However, I find it much easier to reason about code when val implies an immutable reference. Immutability makes code (especially concurrent code) much easier to work with.

As such, I have chosen not to use custom getters for val class properties. If a read-only class property changes value over time, I instead replace that property with a normal function:

class Person(val birthDay: DateTime) {
  fun age(): Int = yearsBetween(birthDay, DateTime.now())
}

This preference is what the Kotlin coding conventions recommends anyways. It states that you should prefer a property over a function only when the underlying algorithm:

  • does not throw
  • has a O(1) complexity
  • is cheap to calculate (or caсhed on the first run)
  • returns the same result over invocations

Overriding the getter and changing the reference violates the last condition, so avoid doing it!



  1. For brevity's sake, in this post "immutable" actually means "immutable reference." You can always reference a mutable object (like an ArrayList). Protecting against mutability in that case requires more advanced techniques than simply using val, which I'm not going into here. ↩︎