Kotlin const vs val: what's the difference and which to choose?
\n\nYou're writing code in Kotlin and suddenly wonder: "What's the actual difference between const and val? Both seem immutable, right?" Great question! This point confuses even experienced developers transitioning from Java or other languages. It's no surprise that on Stack Overflow the topic "What is the difference between 'const' and 'val'?" gets thousands of views. Today we'll settle this once and for all, break down the theory, and back it all up with live code examples.
Ready? Let's go!
\n\nQuick primer: what is val?
\n\nLet's start with the simplest. val (from value) is an immutable reference. You can assign a value to it once and never change it again. Sounds familiar? Yes, it's analogous to final in Java or readonly in C#.
val name: String = "Alice"\nname = "Bob" // Compilation error! Val cannot be reassigned\n\nBut there's an important nuance: val guarantees that the reference won't change, but it does not guarantee the immutability of the object itself. If you use val with a mutable list (MutableList), you can still add and remove elements.
val numbers = mutableListOf(1, 2, 3)\nnumbers.add(4) // Works!\n// numbers = mutableListOf(5, 6) // Error! Cannot reassign the reference\n\nval can be initialized either at declaration or later in the code (e.g., in a constructor or init block). Its value is computed at runtime.
What about const? The strict guardian of compile time
\n\nconst is the stricter sibling of val. It is also immutable, but with a superpower: the value must be known at compile time. In other words, it's a true constant that the compiler can inline directly into the code.
const has strict rules:
- \n
- Can only be declared at the file level (outside a class) or inside an
object/companion object. \n - Must be of a primitive type or
String. \n - Cannot be the result of a function call or computation — only a literal or another constant. \n
// Correct\nconst val PI = 3.14159\nconst val APP_NAME = "MyApp"\n\n// Incorrect\nconst val currentTime = System.currentTimeMillis() // Error! Value not known at compile time\nconst val userName = getName() // Error! Function call is not allowed\n\nKey differences: const vs val in a table
\n\nFor clarity, here's a comparison table:
\n\n| Criterion | \nconst val | \nval | \n
|---|---|---|
| Initialization time | \nAt compile time | \nAt runtime | \n
| Where it can be declared | \nTop-level, object, companion object | \nAnywhere (inside a class, function, constructor) | \n
| Data types | \nOnly primitives and String | \nAny types (including objects, lists) | \n
| Performance | \nMaximum (inlining) | \nStandard | \n
| Usage in annotations | \nYes | \nNo | \n
Practical example: annotations and performance
\n\nOne of the most