Five Kotlin Optimizations That Stop Garbage Before It Ships
These five techniques target the exact failure modes that survive code review and surface in production: silent argument swaps, runaway allocations in hot paths, impossible UI states, and stale async results. Each fix is a local, mechanical change that costs no new dependencies and catches the bug at compile time or prevents it outright.
Hot-path lambdas inside RecyclerView bindings or per-frame callbacks create Function objects and extra allocations; `inline` inlines the body away but can bloat bytecode when applied to large functions. Bare Int and String parameters invite silent argument swaps — `@JvmInline value class` gives them distinct types with near-zero overhead in unboxed contexts. Chaining `map`/`filter`/`first` on a 650-element list builds two full intermediate collections before grabbing one item; `Sequence` evaluates lazily and short-circuits, though it adds wrapping cost that makes it slower for tiny lists. A player state object holding `isLoading`, `isPlaying`, and a nullable `errorMessage` permits impossible combinations; a `sealed interface` hierarchy lets the compiler exhaustively check every `when` branch and flags missing cases the moment a new state is added. Coroutines launched into `GlobalScope` outlive their screens and race to overwrite results — scoping them to `viewModelScope` and cancelling the previous job before starting a new one keeps state consistent, while CPU-bound loops need explicit `ensureActive()` checks to respond to cancellation.
The advice to study Kotlin's own standard-library source for inline usage patterns is a practical heuristic that turns a compiler-flag decision into a learnable taste — look at what the language designers actually inlined.
The `Sequence` vs. eager-collection trade-off is often reduced to a one-liner rule, but the post correctly notes that lambda capture and compiler inlining decisions make per-step allocation behavior non-obvious; the real differentiator is chain length and early termination.
Omitting `else` in a `when` over a sealed type is framed not as a style preference but as a deliberate compile-time safety net — adding `else` silently absorbs new states and defeats the exhaustiveness check that is the whole point of sealing.
The three code-review flags at the end (`!!`, `Dispatchers.IO` for CPU work, `var` in state classes) are presented as recurring anti-patterns rather than one-off tips, suggesting they survive in codebases precisely because they compile and run until a specific failure mode triggers.