skip to content

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

level: middleimportance: must knowfreq 60%

answer

  1. Types: String + 8 primitives, nothing else
  2. Scopes: top-level / object / companion
  3. Initializer must be a constant expression
  4. No function calls, no .length, no runtime
  5. Java sees companion const as static final on outer class

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.

solid answer

~40 s

`const val` accepts only `String` and the primitive types: `Int`, `Long`, `Short`, `Byte`, `Double`, `Float`, `Boolean`, `Char`. Nullable types, arrays, collections, enum entries, and other objects are rejected. Scope is restricted to **top-level** declarations, members of an **`object`**, or members of a **`companion object`** — it cannot be a local variable, a member of a regular class, or have a custom getter or a delegate. The initializer must be a **constant expression**: literals, string concatenation of constants, and arithmetic/logical operations over other `const val`s are allowed, but any function call (including `.length` or `.uppercase()`), property access on a non-const, or runtime value is not. This is why `const val` integrates with places that require compile-time constants, such as annotation arguments.

code

kotlin · 9 lines
kotlin
const val SCHEME = "https://"
const val HOST = "api.example.com"
const val BASE_URL = SCHEME + HOST      // OK: constant expr over consts
const val HOUR_SECONDS = 60 * 60        // OK
// const val HOST_LEN = HOST.length     // ERROR: not a constant expression

class Http {
    companion object { const val OK = 200 }  // Java: Http.OK (static final)
}

go deeper

for a junior

Knows const is for String and number/boolean types and lives at the top level or in a companion object.

for a middle

Lists the exact allowed types and scopes and recognizes that function calls like .length break the constant-expression rule.

for a senior

Explains constant folding of operations over consts and why these restrictions follow from compile-time inlining and the JVM constant pool.

for a principal

Discusses Java interop placement (static final on outer class vs ConfigKt) and API design implications of exposing constants this way.

## Allowed types `const val` is limited to values the compiler can embed as literals: - **`String`** — e.g. `const val NAME = "KataJob"`. - **Primitives:** `Int`, `Long`, `Short`, `Byte`, `Double`, `Float`, `Boolean`, `Char`. Not allowed: nullable types (`String?`), arrays (`IntArray`), collections (`List`), enum entries, `Unit`, or any other object reference. There is no way to declare `const val NOTHING: Any? = null` either — `null` is not a primitive/`String` constant. ## Allowed scopes `const` may appear only in: 1. **Top level** of a file: ```kotlin const val API_VERSION = "v2" ``` 2. Inside an **`object`**: ```kotlin object Limits { const val MAX = 100 } ``` 3. Inside a **`companion object`** (the common way to attach constants to a class): ```kotlin class Http { companion object { const val OK = 200 } } ``` It is **not** allowed as a local variable, as a member of a regular (non-companion) class body, on a function, on a property with a custom getter, or on a delegated property (`by`). ## Constant-expression initializer The value must be a **constant expression** — computable by the compiler without executing program logic: - Literals: `42`, `3.14`, `'A'`, `true`, `"text"`. - Operations on `const` operands: `const val SECONDS = 60 * 60`. - Concatenation of constants: `const val URL = SCHEME + HOST` where both are `const`. Rejected: ```kotlin const val LEN = "abc".length // ERROR: property/function call const val UP = name.uppercase() // ERROR: function call const val CFG = readEnv("PORT") // ERROR: runtime call ``` ## Why these rules exist Because the compiler **inlines** the value into every use site, it must be a single statically-known literal of a type the bytecode can embed directly. Strings and primitives are exactly the values the JVM constant pool can hold. This also makes `const val` usable in **annotation arguments**, **`when` constant branches**, and other compile-time-constant contexts where a plain `val` is rejected. ## JVM interop note A top-level `const val FOO` in file `Config.kt` is compiled into the `ConfigKt` class as a `public static final` field, so Java sees it as `ConfigKt.FOO`. A `const val` in a `companion object` becomes a `static final` field on the **enclosing** class (not the companion), so Java accesses it as `Http.OK`.

  • Can `const val URL = SCHEME + HOST` compile if both operands are `const`?
    Yes. String concatenation of compile-time constants is itself a constant expression, so the compiler folds it into a single literal. If either operand were a plain `val`, it would fail.
  • How does Java access a `const val` declared in a Kotlin `companion object`?
    As a `public static final` field on the enclosing class, e.g. `Http.OK` — not `Http.Companion.OK`. The constant is hoisted to the outer class because it is a compile-time constant.

saying these in an interview costs you the question

  • Saying nullable types or `null` are allowed for `const`
  • Claiming `"abc".length` is a valid `const` initializer
  • Thinking `const` works inside any class body
  • Listing arrays or collections as allowed `const` types
  • Not knowing the initializer must be a constant expression

context