Interview QuestionPractical QuestionFollow-up Questions

Why val Is Read Only and Not Immutable

skydovesJaewoong Eum (skydoves)||8 min read

Why val Is Read Only and Not Immutable

The val keyword in Kotlin is commonly mistaken for a guarantee of immutability. In practice, val only ensures that a reference cannot be reassigned after initialization. It does not prevent modification of the object that the reference points to. This distinction between reference immutability and object mutability is fundamental to understanding Kotlin's type system and avoiding subtle bugs in concurrent or state sensitive code. By the end of this lesson, you will be able to:

  • Explain the difference between a read only reference and an immutable object.
  • Demonstrate how val allows modification of mutable objects through their own APIs.
  • Describe how custom getters can make val properties return different values on each access.
  • Identify strategies for achieving true immutability in Kotlin.

Reference Immutability vs Object Mutability

When you declare a val, the Kotlin compiler prevents reassignment of the reference. The reference itself is fixed, but the object it points to may expose methods that modify its internal state.

val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)  // allowed: the list content changes
// mutableList = mutableListOf(5, 6)  // compile error: reassignment

The reference mutableList always points to the same MutableList instance. However, calling add() on that instance modifies the list contents. The val keyword has no opinion about what happens inside the object.

This behavior is consistent with how final works in Java. A final variable cannot be reassigned, but the object it references can still be modified through its public API.

The distinction becomes critical in concurrent code. A val reference is safe to share between threads because the reference itself cannot change. However, if the referenced object is mutable and multiple threads modify it simultaneously, race conditions can occur despite the val declaration:

val sharedMap = mutableMapOf("key" to "value")
// Thread 1: sharedMap["key"] = "updated"
// Thread 2: sharedMap["key"] = "conflict"
// Race condition despite val

Using an immutable map or a thread safe implementation like ConcurrentHashMap resolves this, but the val keyword alone provides no protection against concurrent modification of the object.

Custom Getters and Computed Properties

A val property with a custom getter can return a different value on every access. This further demonstrates that val does not imply a fixed value.

val currentTime: Long
    get() = System.currentTimeMillis()

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor