Why must annotation arguments (and certain `when`/`switch`-like contexts) use `const val` rather than a plain `val`?
answer
- Annotation args are compile-time metadata
- Plain val = runtime, rejected in annotations
- const val = inlined literal, accepted
- @RequestMapping(PATH) needs const
- Single source of truth for magic strings
basics
~20 sAnnotations are baked into the compiled code, so their values must be known at compile time. A plain val is only set when the program runs, so it can't be used there. const val is known at compile time, so it works.
solid answer
~40 sAnnotation arguments are written into the class file's metadata at **compile time**, so they must be **compile-time constants**. A plain `val` is initialized at runtime and read via a getter, so its value isn't available when the annotation is encoded — the compiler rejects it. `const val` is a compile-time constant whose literal value is known and inlined, so it satisfies the requirement. The same principle applies to other constant-expression contexts: for example, branches that need constant labels or any position the language specifies must be a constant expression. Using `const val` (often in a `companion object`) lets you name these values once and reuse them in annotations like `@RequestMapping(PATH)` or `@Deprecated(MESSAGE)` while keeping a single source of truth.
code
kotlin · 7 linesobject Routes { const val USERS = "/users" }
@RequestMapping(Routes.USERS) // OK
class UserController
val dynamicPath = loadPath()
// @RequestMapping(dynamicPath) // ERROR: must be a compile-time constantgo deeper
Knows annotations need values fixed at compile time and that const val provides them.
Explains that annotation arguments are encoded into class metadata at compile time, so runtime vals are rejected while const val is inlined.
Generalizes to all constant-expression contexts and notes the additional allowed annotation arg kinds (enum, KClass, arrays).
Drives convention: centralize route/header/message constants as const val for a single source of truth, balancing reuse against inlining/binary-compat trade-offs.
## Annotations are compile-time metadata An annotation and its arguments are **encoded into the class file** by the compiler. They are not evaluated at runtime when the annotated element loads — the values are already frozen in the bytecode metadata. Therefore every annotation argument must be a **compile-time constant** (or an array/other annotation/enum/`KClass` thereof). ```kotlin const val PATH = "/users" @RequestMapping(PATH) // OK: PATH is a compile-time constant class UserController ``` A plain `val` cannot be used: ```kotlin val path = "/users" // runtime value, getter-backed @RequestMapping(path) // ERROR: an annotation argument must be a compile-time constant class Broken ``` The compiler reports that the argument must be a constant — because at the moment the annotation is written into the class file, a runtime-initialized `val` simply has no value yet. ## Why `const val` qualifies `const val` is, by definition, a value the compiler resolves and **inlines** as a literal. So when the annotation is encoded, the compiler substitutes the literal directly. This makes `const val` the canonical way to share annotation argument values: ```kotlin object Routes { const val USERS = "/users" const val ORDERS = "/orders" } @RequestMapping(Routes.USERS) class UserController ``` ## Other constant-expression contexts Beyond annotations, the language requires constant expressions in several places. A `const val` works wherever a compile-time constant is needed, while a plain `val` does not. The unifying rule is: **if the construct is resolved at compile time, it needs a compile-time constant, i.e. `const val` (String/primitive) — not a runtime `val`.** ## Practical benefit Defining route paths, header names, or deprecation messages as `const val` gives a **single source of truth** that is simultaneously usable in normal code and in annotations, avoiding magic-string duplication while satisfying the compile-time-constant requirement.
- Can you pass an enum entry or a `KClass` as an annotation argument even though it's not `const`?Yes. The constant-expression rule for `const val` covers String/primitives, but annotation arguments may also be enum constants, `KClass` references (`SomeClass::class`), other annotations, and arrays of these — they are all resolvable at compile time.
- Why doesn't a plain `val` initialized with a literal work in an annotation?Even if its initializer is a literal, a plain `val` is still a runtime property read through a getter, so the compiler treats it as a runtime value, not a compile-time constant. You must mark it `const`.
saying these in an interview costs you the question
- Saying a plain `val` works in annotations if it has a literal initializer
- Not knowing annotation arguments are encoded at compile time
- Confusing const with runtime field reads
- Claiming you cannot share constants in annotations at all
- Thinking enum/KClass args require const (they're allowed as constants too)