The question every declaration asks
Every time you create a name in Kotlin, you choose between two keywords. The choice answers one question: will this value ever change?
valdeclares a read-only name. It is assigned once and can't be reassigned.vardeclares a variable. Its value may be replaced later.
This is more than style. With val, the compiler guarantees that no line of the program changes the name, and it refuses to build code that tries. A whole category of bugs, a value changed where nobody expected it, becomes impossible for that name.
val pi = 3.14159 // never changes: val
var lives = 3 // goes down when the player is hit: var
lives -= 1
println(lives) // 2
No types are written. Kotlin infers Double for pi and Int for lives from the values. Both keywords work with inference and with explicit types such as val pi: Double = 3.14159.
Which one to pick
Kotlin programmers start with val and switch to var only when a value genuinely needs to change: a counter, a running total, a position that moves. If you declare something with var and never reassign it, the IDE suggests changing it to val, because the stricter keyword communicates more.
The error you will meet
Sooner or later you will reassign a val. The compiler's message is precise:
error: 'val' cannot be reassigned.
maxLives = 5
^^^^^^^^
The carets underline the name that was declared with val. There are two honest ways out: keep val and stop reassigning, or change the declaration to var because the value really must change. Compound operators hit the same wall. maxLives += 1 fails with the same message, because += is a reassignment in disguise.
The exercise, step by step
The program sets up a tiny game with one value that never changes and one that does. The expected output is:
maxLives = 3
score = 15
- Declare
maxLiveswithval, set to3. The rules of the game are fixed, so the compiler should enforce that. The type isInt, inferred, so no annotation is needed. - Print it with a string template,
println("maxLives = $maxLives"). Read-only means the value can't be changed, not that it can't be read. A name followed by$inside a string is replaced by its value; a longer expression takes braces, as in${a + b}. - Declare
scorewithvar, starting at0. A running total has to change, and onlyvarallows that. - Add 10 with
+=.score += 10is shorthand forscore = score + 10. The compiler accepts it only becausescoreis avar. - Add 5 more the same way. A
varcan be updated as many times as you need, and each+=reads the current value and stores the new one. - Print the total with
println("score = $score"). By nowscoreholds 15 whilemaxLivesis still 3.
val maxLives = 3
println("maxLives = $maxLives")
var score = 0
score += 10
score += 5
println("score = $score")
If score were declared with val, the line score += 10 would stop the build with the reassignment error above.
Details and common mistakes
- Reassigning a
val. The usual cause is a declaration that should have beenvar. Ask yourself if the value really has to change before you switch it. - Using
vareverywhere. The program works, but it loses the guarantee. Readers must check every later line to know whether a value can change. - The type is fixed either way. A
varholding anIntcan't later receive a string or a decimal such as5.5.varallows a new value of the same type, not a new type. valprotects the name, not the object. Avalthat refers to a mutable list can't be pointed at another list, but its contents can still change withadd.- Order of declaration. A name must be declared before the lines that use it.
Key takeaways
valis read-only after the first assignment;varcan be reassigned.- The compiler enforces the difference:
'val' cannot be reassigned. - Default to
val, and usevarfor counters, totals and anything else that must change. +=counts as a reassignment, so it needsvar.