Question

var vs val in Kotlin: Mutable and Read-Only References Explained

kotlinvariables

Question

In Kotlin, what is the exact difference between var and val?

I read the Kotlin documentation about properties and fields, which says that a read-only property declaration:

  • starts with val instead of var
  • does not allow a setter

However, the documentation also shows code like this:

fun copyAddress(address: Address): Address {
    val result = Address() // there's no 'new' keyword in Kotlin
    result.name = address.name // accessors are called
    result.street = address.street
    // ...
    return result
}

This confused me because result is declared with val, but its properties are still being changed.

So:

  • What is the exact difference between var and val in Kotlin?
  • Why does Kotlin need both?
  • Why is it valid to modify result.name and result.street when result itself is declared with val?

Short Answer

By the end of this page, you will understand that val means the reference cannot be reassigned, while var means it can. You will also see why a val object can still have mutable internal properties, how this differs from full immutability, and how Kotlin developers use val and var in real code.

Concept

In Kotlin, val and var describe whether a variable or property reference can be reassigned.

Core idea

  • val = read-only reference
  • var = mutable reference

That means:

val name = "Ava"
// name = "Liam" // not allowed

var city = "Paris"
city = "Rome" // allowed

What val really means

A val does not mean the object itself is deeply immutable. It only means that the variable now always points to the same object.

If that object has mutable properties, those properties can still change.

For example:

class Address {
    var name: String = ""
    var street: String = ""
}

val result = Address()
result.name = "Sam"     // allowed
result.street = "Main"  // allowed
// result = Address()     // not allowed

Here, result always refers to the same Address instance, but the inside of that instance can still be modified because name and street are declared as var.

Why this matters

This distinction is important in real programming because it separates two different ideas:

  1. Can I point this variable to a different object?
  2. Can the object itself change internally?

Kotlin lets you control both.

  • Use val when the reference should stay fixed.
  • Use var when reassignment is part of the logic.
  • Use val properties inside a class when the object's state should not change after creation.

About the documentation example

The documentation is not saying that a val object can never be changed in any way. It is saying that a val property itself has no setter.

So this is illegal:

val result = Address()
// result = anotherAddress

But this is legal if the Address class has mutable properties:

result.name = address.name

That line is not reassigning result. It is modifying a property on the object that result refers to.

Mental Model

Think of a variable as a label attached to a box.

  • val means the label must stay attached to the same box.
  • var means you can move the label to a different box later.

Now think about the contents of the box.

Even if the label is fixed (val), the contents inside the box may still be changed.

Analogy

val box = Address()

This means:

  • you must keep using the same box
  • but you may still put different things inside it, if the box allows that

So:

box.name = "Alice"

changes the contents of the box.

But:

box = Address()

tries to replace the entire box, which val does not allow.

Easy rule to remember

  • val protects the reference
  • var allows changing the reference
  • object mutability depends on the object's own properties, not just on val or var
Take Quiz

Syntax and Examples

Basic syntax

val x = 10
var y = 20
  • x cannot be reassigned
  • y can be reassigned

Example 1: Primitive-like values

val age = 25
// age = 26 // error

var score = 100
score = 120 // okay

Example 2: Object references

class User {
    var name: String = ""
}

val user = User()
user.name = "Maya" // okay
// user = User()    // error

Explanation

  • user is a val, so it must keep pointing to the same User object.
  • name is a var property inside User, so it can change.

Example 3: Truly read-only object state

data class Person(val name: String, val age: Int)

val person = Person("Nina", 30)
// person.name = "Eva" // error
// person = Person("Eva", 31) // error

Here both levels are read-only:

  • person is a val
  • Person properties are also val

So neither the reference nor the object's fields can be changed.

Example 4: var property vs val property in a class

class Counter {
    var count: Int = 0
    val label: String = "Clicks"
}
  • count has a getter and setter
  • label has only a getter

That is what the documentation means when it says a val property does not allow a setter.

Step by Step Execution

Consider this code:

class Address {
    var name: String = ""
    var street: String = ""
}

fun copyAddress(address: Address): Address {
    val result = Address()
    result.name = address.name
    result.street = address.street
    return result
}

Step-by-step

1. A new Address object is created

val result = Address()
  • Kotlin creates a new Address instance.
  • result stores a reference to that object.
  • Because result is val, that reference cannot later point to another Address.

2. The name property is copied

result.name = address.name
  • Kotlin reads address.name.
  • Kotlin calls the setter for result.name.
  • This changes the name property inside the object referred to by result.
  • result itself is not reassigned.

3. The street property is copied

result.street = address.street
  • Same idea as above.
  • The object changes internally.
  • The reference stored in result stays the same.

4. The object is returned

return result
  • The same Address object is returned.

What would be illegal?

fun badCopy(address: Address): Address {
    val result = Address()
    // result = address // error
    return result
}

This is illegal because it tries to reassign the result variable itself.

Short trace

LineWhat changes?
val result = Address()result starts pointing to one new object
result.name = ...object state changes
result.street = ...object state changes
return resultsame object is returned

Real World Use Cases

1. Safer local variables

Most local variables do not need reassignment.

val response = api.getUser()
val parsed = parseUser(response)

Using val makes code easier to reason about because you know those names will not suddenly point somewhere else later.

2. Mutable app state where needed

Sometimes reassignment is required.

var currentPage = 1
currentPage += 1

This is a good use of var because the value is expected to change.

3. Working with mutable objects

You often keep one reference fixed while changing its contents.

val cart = mutableListOf("Book")
cart.add("Pen")
cart.add("Notebook")

cart is still the same list reference, but the list contents change.

4. Domain models with immutable fields

For data you do not want changed accidentally:

data class Order(val id: String, val total: Double)

This helps avoid bugs in business logic.

5. Configuration values

val baseUrl = "https://api.example.com"

Configuration constants are usually val because they should not be reassigned during execution.

Take Quiz

Real Codebase Usage

In real Kotlin projects, developers usually prefer val by default and switch to var only when mutation is necessary.

Common patterns

Prefer val for local variables

val user = repository.findById(id)
val dto = user.toDto()
return dto

This reduces accidental reassignment.

Use var for changing state

var retries = 0
while (retries < 3) {
    retries++
}

Use immutable data models when possible

data class User(val id: Int, val name: String)

When data must change, Kotlin code often creates a new object instead of mutating the old one.

data class User(val id: Int, val name: String)

val user = User(1, "Ana")
val updatedUser = user.copy(name = "Anna")

Guard clauses and validation

Developers often store validated values in val variables after checks.

fun register(name: String?) {
    require(!name.isNullOrBlank())
    val cleanName = name.trim()
    println(cleanName)
}

Mutable collections behind read-only references

val items = mutableListOf<String>()
items.add("A")

This is common when the collection contents should change, but the variable should keep referring to the same collection.

Practical rule used by teams

  • Start with val
  • Change to var only if reassignment is actually needed

Common Mistakes

1. Thinking val means fully immutable

This is the most common mistake.

val numbers = mutableListOf(1, 2, 3)
numbers.add(4) // allowed

numbers is read-only as a reference, but the list object is mutable.

How to avoid it

Check both:

  • is the reference val or var?
  • is the object itself mutable or immutable?

2. Confusing property mutation with variable reassignment

Broken mental model:

val user = User()
user.name = "Lee" // some beginners expect error here

This is valid if name is a mutable property.

How to avoid it

Remember: user.name = ... changes the object, not the user reference.

3. Using var everywhere

var name = "Sara"
var age = 20

If values never change, var adds unnecessary mutability.

Better

val name = "Sara"
val age = 20

4. Expecting a val class property to have a setter

Broken code:

class Product {
    val id: Int = 1
}

fun main() {
    val product = Product()
    // product.id = 2 // error
}

A val property only has a getter.

5. Assuming val and var are about memory or performance

They are mainly about correctness and mutability rules, not about manually controlling memory.

6. Mixing up read-only interfaces and immutable objects

val names: List<String> = mutableListOf("A", "B")

This gives a read-only view through List, but the underlying object may still be mutable elsewhere.

How to avoid it

Do not assume read-only access always means the object can never change.

Comparisons

val vs var

Featurevalvar
Can be reassigned?NoYes
Has getter?YesYes
Has setter?NoYes
Best default choice?YesOnly when needed
Guarantees object immutability?NoNo

Reference mutability vs object mutability

SituationAllowed?Why?
Reassign a val variableNoreference is fixed
Change a mutable property inside a val objectYesobject state can change
Reassign a var variableYesreference is mutable
Change a val property inside a classNoproperty has no setter

Kotlin vs Java intuition

KotlinRough Java idea
val local variablesimilar to final reference
var local variablenormal reassignable variable

The Java comparison is helpful, but not perfect. In Kotlin, val is the language's standard way to express a read-only reference.

Take Quiz

Cheat Sheet

Quick rules

  • val = cannot reassign the variable/property
  • var = can reassign the variable/property
  • val does not automatically make the object immutable
  • A val property has a getter only
  • A var property has a getter and setter

Basic syntax

val a = 10
var b = 20

Object example

val user = User()
user.name = "Kai" // okay if name is var
// user = User()   // error

Class property example

class Example {
    val x = 1
    var y = 2
}
  • x cannot be assigned after initialization
  • y can be changed later

Best practice

  • Use val by default
  • Use var only when reassignment is required

Important edge case

val list = mutableListOf(1, 2)
list.add(3) // allowed

val protects the reference, not the contents.

FAQ

Why can I change properties of a val object in Kotlin?

Because val only prevents reassigning the reference. If the object has mutable (var) properties, those properties can still be changed.

Is val the same as immutable in Kotlin?

No. val means read-only reference, not deep immutability.

Is val similar to final in Java?

Yes, for references it is very similar. A val reference cannot be pointed to another object after assignment.

Should I always use val instead of var?

Use val by default. Change to var only when your logic needs reassignment.

Can a class have both val and var properties?

Yes. This is very common. Some fields stay fixed, while others are allowed to change.

Why does Kotlin provide both val and var?

Because programs need both stable references and mutable state. The two keywords make that choice explicit and readable.

Take Quiz

Mini Project

Description

Build a small Kotlin program that demonstrates the difference between changing a reference and changing the contents of an object. This helps make the val vs var distinction concrete using a simple profile model.

Goal

Create a program that shows: a val reference can keep the same object while its mutable properties change, and a var reference can be reassigned to a different object.

Requirements

  • Create a class with at least one var property and one val property.
  • Declare one object using val and modify only its mutable property.
  • Declare another object using var and reassign it to a new instance.
  • Print the results so the difference is visible.
Take Quiz

Keep learning

Related questions

View more