What expressions can supply an annotation argument or its default value? Explain the role of const val.
answer
- Compile-time constant expression only
- const val, literals, enum, Foo::class, nested annotation, array literal
- Plain val and function calls are rejected
- Constant arithmetic on constants allowed
- Defaults obey the same rule
basics
~10 sOnly compile-time constants: literals, enum entries, and values declared with const val. A normal val or a function result won't compile because the value must be known when the code is compiled.
solid answer
~40 sEvery annotation argument — and every parameter default declared with `= ...` — must be a **compile-time constant expression**. Valid sources are: literals (`42`, `"x"`, `true`), enum entries (`Level.HIGH`), class literals (`Foo::class`), nested annotation constructions (`Inner(1)`), array literals (`[1, 2]`/`arrayOf(...)`), and references to **`const val`** properties. A plain `val name = "x"` is rejected because it is a runtime-initialized property, even though its value looks constant; only `const val` (top-level or in an `object`/`companion object`, of a primitive or String type) is a true compile-time constant. Constant arithmetic on constants is allowed (`const val MAX = 10 * 60`). Defaults follow the exact same rule — you declare them with `val retries: Int = DEFAULT_RETRIES` where `DEFAULT_RETRIES` is a `const val`. There is no `lateinit`, no computed defaults, and no calls to ordinary functions.
code
kotlin · 9 linesobject Defaults { const val TIMEOUT_MS = 30 * 1000 }
annotation class Timeout(val ms: Long = Defaults.TIMEOUT_MS.toLong())
@Timeout // baked-in default
fun a() {}
@Timeout(ms = 5000) // literal override
fun b() {}go deeper
Knows literals and const val work and a plain val/function call does not.
Articulates the compile-time-constant rule and the const val requirement (primitive/String, top-level or object).
Explains inlining of const val into metadata and that defaults obey the same constraint, including constant arithmetic.
Designs annotation defaults centralized in const objects for consistency, anticipating how processors read inlined constants.
## The single rule An annotation argument must be a **compile-time constant expression** — a value the compiler can fully evaluate without running any code. The same rule applies to a parameter's **default value** declared with `= ...`. ## What qualifies as a compile-time constant - **Literals**: `1`, `1L`, `3.14`, `'c'`, `"text"`, `true`. - **`const val` references** — properties declared `const val`, which must be top-level or inside an `object`/`companion object` and of a primitive type or `String`. - **Enum entries**: `DeprecationLevel.ERROR`. - **Class literals**: `Foo::class`. - **Nested annotation calls**: `Inner(min = 1)`. - **Array literals**: `[1, 2, 3]` (allowed inside annotations) or `arrayOf(1, 2, 3)`. - **Constant expressions over the above**: `const val MINUTE = 60`; `@Timeout(MINUTE * 5)` is fine because both operands are constants. ## What does NOT qualify - A plain `val x = "y"` — a normal property is initialized at runtime, so it is **not** a compile-time constant, even if its value is literally fixed. - A function call: `@Name(buildName())` — fails, runtime evaluation. - `System.getenv(...)`, `Math.PI` used where a strict constant is required (it's a `val`, not `const`), etc. ```kotlin const val DEFAULT_RETRIES = 3 // compile-time constant val runtimeRetries = compute() // NOT a constant annotation class Retry(val times: Int = DEFAULT_RETRIES) // default OK @Retry // uses default 3 class A @Retry(times = 5) // literal OK class B // @Retry(times = runtimeRetries) // COMPILE ERROR: not a const ``` ## Why `const val` exists for this `const val` tells the compiler the value is known at compile time and can be **inlined** into call sites and into class-file metadata. That is exactly what annotations need. A normal `val` only guarantees the reference is assigned once at runtime — too late for annotation metadata. ## Defaults are not special A default such as `val level: Level = Level.LOW` is just another annotation-constant slot; it obeys the identical constraint. Omitting the argument at a use site makes the compiler bake the default constant into the metadata.
- Why can't a regular val be used as an annotation argument?A regular val is initialized at runtime, so its value is not known at compile time; only const val (and literals/enums) qualify.
- Is const val MAX = 10 * 60 a valid constant for an annotation?Yes; arithmetic over compile-time constants is itself a compile-time constant.
Like a form that only accepts pre-printed checkbox options — you can't write a formula and have the clerk compute it later.
saying these in an interview costs you the question
- Saying any val works as an annotation argument
- Confusing const val with normal val
- Thinking defaults can call functions or read runtime state
- Believing System.getenv or similar can supply a default
- Not knowing enum entries and class literals are valid constants