When migrating a codebase from `values()` to `entries`, what concrete behavioral and API differences could break or surprise you?
answer
- Same order/contents; type changes Array -> List
- Spread *entries fails; use entries.toTypedArray()
- Mutation of result no longer compiles (exposes smells)
- entries === entries (cached); values() !== values()
- Needs Kotlin 1.9+; Java keeps values()
basics
~20 sentries is a List, not an Array, so array-only operations and code that mutated the result will break. The shared instance also means you must never try to modify it. Order and contents stay the same.
solid answer
~40 sThe contents and order are identical, but the static type changes from `Array<E>` to `List<E>` (`EnumEntries<E>`). Breakages cluster around that: code calling array members (`.size` is fine on both, but `Arrays.copyOf`, spread `*values()`, array-typed parameters, or Java callers expecting `E[]`) won't accept `entries` directly — you'd convert with `entries.toTypedArray()`. Code that previously **mutated** the `values()` copy (legal on an array) is now illegal on the immutable list — which often reveals a latent bug. Also, `entries` is the same cached instance every call, so referential identity differs from `values()` (each call returned a distinct array). For Java interop, `entries` is exposed but Java sees a `List`; `MyEnum.values()` remains for Java. Reflection paths (`Class.getEnumConstants()`) are unaffected. Finally, `entries` needs Kotlin 1.9+ and the language version set accordingly.
code
kotlin · 8 linesenum class Op { ADD, SUB, MUL }
// Safe migration
val names = Op.entries.map { it.name } // was Op.values().map { ... }
// Boundary needing an array (e.g. vararg/Java interop)
fun consume(vararg ops: Op) {}
consume(*Op.entries.toTypedArray()) // convert at the boundarygo deeper
Knows entries returns a List and most loops migrate cleanly; may miss the boundary cases.
Identifies spread/array-parameter breakage and the conversion via toTypedArray().
Covers referential identity, Java interop, mutation-now-illegal exposing bugs, and the 1.9 version gate.
Plans a staged migration with codemods/inspections, defines boundary conventions, and weighs cross-module/version constraints.
## What does NOT change - **Order**: declaration order, identical to `values()`. - **Contents**: exactly the same constants. - **`valueOf` / `name` / `ordinal`**: unaffected. ## What DOES change — the type `values()` → `Array<E>`. `entries` → `EnumEntries<E>` which is a **read-only `List<E>`**. Most pitfalls come from array-vs-list: - **Spread operator**: `someVararg(*MyEnum.values())` works (array). `*MyEnum.entries` does **not** compile — convert with `MyEnum.entries.toTypedArray()`. - **Array-typed APIs / Java methods** expecting `E[]` won't accept a `List`. Convert or keep `values()` at that boundary. - **Mutation**: `values()` returned a mutable array, so `arr[0] = X` compiled (and corrupted only that copy). `entries` is immutable — such code now fails to compile, often exposing a pre-existing smell. - **Referential identity**: `values() !== values()` (new array each call). `entries === entries` (same cached instance). Any code relying on a fresh-object identity per call changes behavior. ```kotlin enum class Suit { CLUBS, DIAMONDS, HEARTS, SPADES } fun needsArray(vararg s: Suit) { /* ... */ } needsArray(*Suit.values()) // OK: spread of array // needsArray(*Suit.entries) // does NOT compile needsArray(*Suit.entries.toTypedArray()) // convert when an array is required ``` ## Interop & tooling - **Java callers**: keep using `Suit.values()` (still generated). `entries` is also accessible but typed as `List`. - **Reflection**: `Class.getEnumConstants()` is independent of either and keeps working. - **Kotlin version**: `entries` is stable in **1.9**; ensure `languageVersion`/`apiVersion` allow it. Older modules can't use it. - **IDE migration**: IntelliJ offers an inspection/quick-fix to replace `values()` with `entries`; review each site for the array-vs-list cases above rather than blind replace-all. ## Migration checklist 1. Replace `for`/`map`/`filter` over `values()` with `entries` (safe, common case). 2. At array boundaries (spread, Java `E[]`, array params) either keep `values()` or use `.toTypedArray()`. 3. Delete now-illegal mutation of the result (and fix the underlying intent). 4. Confirm Kotlin ≥ 1.9 for the module. ## Why it's worth doing Idiomatic, allocation-free, List-typed (composes with the collections API). The migration is mostly mechanical; the only real thought is at array boundaries.
- Why might replacing `values()` with `entries` suddenly cause a compile error?Because code spread it as an array (`*values()`), passed it to an `Array<E>`/Java `E[]` parameter, or mutated the returned array — all illegal on the immutable `List` that `entries` returns.
- Does `entries` change referential identity semantics?Yes — `entries` is a cached single instance (`===` holds across calls), whereas each `values()` call returned a distinct array.
saying these in an interview costs you the question
- Assuming a blind `values()` → `entries` find-replace is always safe.
- Not anticipating spread-operator / array-parameter breakage.
- Forgetting Java callers and the Kotlin 1.9 requirement.
- Claiming order or contents change.
- Missing that mutation of the old array result is no longer allowed.