skip to content

const & Compile-Time Constants

const val marks a compile-time constant that gets inlined into every use site, limited to strings and primitives at the top level or inside an object or companion. Interviewers contrast it with a plain val, which is a real property with a getter and can be changed without recompiling callers.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is `const val` in Kotlin, and how does it differ from a regular `val`?

level: juniorimportance: must knowfreq 70%

answer

  1. const = compile-time inlined literal
  2. String + primitives only
  3. top-level / object / companion only
  4. val = runtime, getter-backed
  5. needed for annotation arguments

basics

~20 s

const val is a value the compiler knows at compile time and copies directly into the code that uses it. A plain val is set when the program runs and is read through a hidden getter method.

solid answer

~40 s

`const val` declares a compile-time constant: its value is known by the compiler and inlined directly at every use site, so no field read or getter call happens at runtime. It is restricted to `String` and primitive types (`Int`, `Long`, `Double`, `Boolean`, `Char`, etc.) and the initializer must be a constant expression. It can only live at the top level, in an `object`, or in a `companion object` — never on a local variable or inside a regular class body. A plain `val` is a read-only property: it is initialized at runtime and accessed via a synthesized getter, can be any type, and may even be backed by custom getter logic. So `const` is about compile-time inlining and a fixed primitive/String value; `val` is about runtime immutability of a reference.

code

kotlin · 11 lines
kotlin
const val MAX_RETRIES = 3          // compile-time, inlined as 3
val runtimeMax = readConfig()      // runtime value, getter-backed

object Config {
    const val BASE_URL = "https://api.example.com"  // OK
}

class Service {
    // const val X = 1   // ERROR: not allowed in a regular class body
    val x = 1            // OK: plain read-only property
}

go deeper

for a junior

Knows const val is a fixed value known at compile time, limited to String/primitives, vs a val set at runtime.

for a middle

Explains inlining at use sites, the getter-backed nature of plain val, and the scope restrictions (top-level/object/companion).

for a senior

Connects const to constant-expression requirements (annotations, when), and discusses why custom getters and arbitrary types are disallowed.

for a principal

Raises the binary-compatibility hazard of inlined constants across separately-compiled modules and weighs const vs val for public API surfaces.

## What `const val` means `const val` declares a **compile-time constant**: a value the Kotlin compiler fully resolves while compiling, then **inlines** (copies the literal) into every place the constant is referenced. There is no runtime field access and no getter call. ```kotlin const val MAX_RETRIES = 3 fun attempt() { repeat(MAX_RETRIES) { /* ... */ } // compiler emits repeat(3) } ``` At the bytecode level the literal `3` is substituted at the call site, exactly as if you had written `repeat(3)`. ## How a plain `val` differs A `val` is a **read-only property**, not necessarily a constant: - It is initialized **at runtime** (when the object/class loads or is constructed). - It is accessed through a **synthesized getter** (e.g. `getMaxRetries()`), so each read is a method call (the JIT may inline it, but the source semantics are a getter). - It can hold **any type** (objects, collections, nullable types). - It can have a **custom getter**, so its value may be computed each access. ```kotlin val maxRetries = computeFromConfig() // runtime value, getter read val timestamp get() = System.currentTimeMillis() // recomputed each access ``` ## Restrictions on `const` `const` is allowed only when ALL of these hold: - The type is `String` or a **primitive** (`Int`, `Long`, `Short`, `Byte`, `Double`, `Float`, `Boolean`, `Char`). Not `null`, not arrays, not other objects, not enum values. - The initializer is a **constant expression**: literals and operations on other `const` values are fine; function calls and runtime values are not. - It is declared at the **top level**, in an `object`, or in a `companion object`. It cannot be a local variable, a member of a regular class, or have a custom getter. - It cannot be backed by a delegate (`by`). ```kotlin const val GREETING = "Hello" const val NAME_LEN = GREETING.length // ERROR: function/property call, not a constant expression const val SCALED = 60 * 60 // OK: constant expression on literals ``` ## Why it matters - **Annotations:** annotation arguments must be compile-time constants, so `const val` (not plain `val`) can be used, e.g. `@RequestMapping(PATH)`. - **`when` branches / `switch`-like contexts** and other places requiring constant expressions accept `const val`. - **No runtime overhead** for the read, and the value is embedded even for callers compiled separately. ## Gotcha: binary compatibility Because the value is **inlined into callers**, changing a published `const val` and recompiling only the library will NOT update callers that were compiled against the old value until **they** are recompiled. A plain `val` reads the current field, so a recompiled library updates everyone.

  • Why can't you put `const val` on a property inside a normal (non-companion) class?
    A regular class member is tied to an instance and initialized at construction (runtime), so it can't be a compile-time constant. `const` requires a single, statically-known value, which is why it's limited to top-level, `object`, or `companion object` scopes.
  • Does a plain `val` guarantee the value never changes?
    It guarantees you can't reassign the reference, but a `val` with a custom getter can return different values each call, and a `val` pointing to a mutable object (e.g. a `MutableList`) can still have its contents change.

const val is like a value printed directly onto every page (copied everywhere); a plain val is like a phone number you look up in a directory each time.

saying these in an interview costs you the question

  • Claiming `const val` works on any type or on objects/collections
  • Saying `const` can be declared as a local variable
  • Thinking `const val` and `val` are identical at the bytecode level
  • Believing a plain `val` is always a compile-time constant
  • Not knowing `const` requires top-level/object/companion scope

context

open as a page

What types and scopes does `const val` allow, and what must its initializer look like?

level: middleimportance: must knowfreq 60%

basics

~20 s

const val only works for text (String) and basic number/boolean/char types. You can declare it at the top of a file, in an object, or in a companion object. Its value must be something the compiler can figure out without running code.

open as a page

Why must annotation arguments (and certain `when`/`switch`-like contexts) use `const val` rather than a plain `val`?

level: middleimportance: should knowfreq 40%

basics

~20 s

Annotations 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.

open as a page

A library exposes a public `const val VERSION = "1.0"`. The maintainer bumps it to `"1.1"` and republishes only the library JAR. Why might consumers still see `"1.0"`, and how does this differ from a plain `val`?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Because const values get copied directly into the code that uses them at compile time. Consumers that were already compiled still carry the old copied-in value until they are recompiled. A plain val is read fresh from the library each run, so updating the library is enough.

open as a page

Where do `const val` declarations end up in JVM bytecode for top-level, `object`, and `companion object` cases, and how does that affect Java interop and `@JvmStatic`?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

A const val becomes a fixed static final field in the compiled class. Top-level ones live on the file's generated class; companion ones live on the outer class. Java can read them directly by name, and you don't need @JvmStatic.

open as a page