How does a top-level/companion `const val` differ from a regular `val` when accessed from Java?
answer
- const = public static final compile-time constant
- Java reads Foo.NAME directly, value inlined
- Only primitives + String, constant initializer
- Regular companion val -> Foo.Companion.getX()
- const inlining means recompile consumers on change
basics
~20 sA const val becomes a real compile-time constant field that Java reads directly by name. A regular val in a companion object is reached through a getter and the Companion reference, so Java needs more ceremony.
solid answer
~40 s`const val` produces a `public static final` field with a true compile-time constant value, so Java reads it directly as `ClassName.NAME` (and the value is inlined into callers). It bypasses accessors entirely — there is no getter. It is only allowed for primitives and `String`, must be initialized with a constant expression, and must be top-level, in an `object`, or in a `companion object`. A regular `val` in a companion object compiles to an instance member with a getter, so naive Java access is `ClassName.Companion.getName()` — unless you add `@JvmStatic` (generates a static getter) or `@JvmField` (exposes a static field, but not a compile-time constant and not usable in Java `switch`/annotations). So `const` is the cleanest, accessor-free, inlined constant for Java.
code
kotlin · 5 linesobject Limits {
const val MAX = 100 // Java: Limits.MAX (inlined constant)
val current = compute() // Java: Limits.INSTANCE.getCurrent()
}
fun compute() = 5go deeper
Knows const lets Java read Foo.NAME directly without a getter.
States the type/placement/constant-expression rules and contrasts with a companion val's getter access.
Explains inlining and the recompile-consumers ABI risk and when to prefer @JvmField vs const.
Treats public consts as part of the published ABI and sets guidance on which constants are safe to expose vs keep behind accessors.
## Regular `val` (companion object) ```kotlin class Api { companion object { val TIMEOUT = 30 } } ``` The companion is an instance. `TIMEOUT` becomes a field on the `Companion` object plus a getter. From Java: ```java int t = Api.Companion.getTIMEOUT(); ``` The value is **not** a compile-time constant and is fetched at runtime through a getter. ## `const val` ```kotlin class Api { companion object { const val TIMEOUT = 30 } } ``` `const` makes it a **`public static final` compile-time constant**. From Java: ```java int t = Api.TIMEOUT; // direct, no getter, value inlined at compile time ``` Because it is a true constant, Java can use it in `switch` labels, annotation arguments, and other constant contexts. No accessor method is generated. ## Where `const` is allowed and its rules - Type must be a **primitive** or **`String`**. - Initializer must be a **constant expression** (no function calls, no runtime computation). - Placement: **top-level**, inside an **`object`**, or inside a **`companion object`** — not on a plain instance property. - It is implicitly `static` and `final` on the JVM. ## Contrast with `@JvmField` - `@JvmField val X = compute()` on a companion gives a `static` field, but the value is **not** a compile-time constant (not inlined, not usable in annotations/switch). - `const val` is inlined and usable in constant contexts. ## Inlining caveat Because `const` values are inlined into callers' bytecode, changing a published `const` requires **recompiling consumers** to pick up the new value — an ABI consideration for library authors. ## Summary table - regular `val` (companion): instance field + getter -> `Foo.Companion.getX()`. - `@JvmStatic val`: static getter -> `Foo.getX()`. - `@JvmField val`: static field -> `Foo.X` (runtime value, not a true constant). - `const val`: `public static final` compile-time constant -> `Foo.X` (inlined).
- Why can't you write `const val now = System.currentTimeMillis()`?const requires a compile-time constant expression; a runtime function call is not constant, so it is rejected.
- What is the binary-compatibility risk of a public `const`?Its value is inlined into every consumer's bytecode, so changing it requires recompiling all consumers, not just re-linking.
saying these in an interview costs you the question
- Saying const generates a getter
- Claiming const works for any object type, not just primitives/String
- Forgetting that companion val needs Companion.getX() from Java
- Not knowing const values are inlined into callers