What is the difference between a `typealias` and a `@JvmInline value class` in Kotlin, and when would you choose each?
answer
- typealias = transparent synonym, no new type
- value class = distinct wrapper, real type safety
- @JvmInline inlines the single val → no allocation
- boxes when nullable / generic / behind interface
- alias for readability, value class for domain identity
basics
~20 sA typealias is just a nickname for an existing type, so it is interchangeable with that type. A value class is a brand-new wrapper type that the compiler keeps separate, so you cannot mix it up with the underlying type.
solid answer
~40 s`typealias Name = String` introduces a *transparent synonym*: `Name` and `String` are the exact same type, fully assignable in both directions, with zero type safety added. It is purely a readability/shortening tool that disappears at compile time. `@JvmInline value class UserId(val raw: String)` creates a *distinct* type: you cannot pass a `String` where a `UserId` is expected (or vice versa) without wrapping/unwrapping, so the compiler catches mix-ups. At runtime Kotlin usually inlines the single property, so there is no wrapper object allocated — you get type safety at (mostly) zero cost. Choose `typealias` to shorten long generic/function types; choose a value class to model a domain concept (UserId, Email, Money) where you want the compiler to prevent confusing it with a raw String/Int.
code
kotlin · 13 linestypealias Celsius = Double // transparent synonym
@JvmInline
value class Fahrenheit(val deg: Double) // distinct type
fun freezing(c: Celsius) = c <= 0.0
val t: Double = -5.0
freezing(t) // OK: Celsius == Double
// fun heat(f: Fahrenheit)
// heat(t) // ERROR: Double is not Fahrenheit
val f = Fahrenheit(32.0)
val raw: Double = f.deg // unwrap explicitlygo deeper
States the core distinction: typealias is interchangeable, value class is a separate type the compiler keeps distinct.
Adds that @JvmInline inlines the single property to avoid allocation and gives concrete use-case choices.
Explains exactly when a value class still boxes (nullable/generic/interface) and the single-val/no-var constraints.
Frames value classes as zero-cost domain modeling and weighs API/binary-compat and boxing trade-offs versus plain aliases.
## The two tools **`typealias`** declares an *alternative name* for a type. It creates **no new type** — the alias and the aliased type are completely interchangeable. ```kotlin typealias UserName = String fun greet(name: UserName) = "Hi $name" val s: String = "Ada" greet(s) // OK — UserName *is* String val n: UserName = s // OK — assignable both ways val back: String = n // OK ``` Because it is transparent, a `typealias` adds **zero type safety**. If two different concepts both alias `String`, the compiler will happily let you swap them. **`@JvmInline value class`** declares a **distinct wrapper type** around a single value. ```kotlin @JvmInline value class UserId(val raw: String) fun load(id: UserId) { /* ... */ } val s: String = "u-1" // load(s) // COMPILE ERROR — String is not UserId load(UserId(s)) // OK — must wrap explicitly val back: String = UserId(s).raw // unwrap via the property ``` `UserId` and `String` are **different types**; assignment is not automatic in either direction. This is the type safety: you cannot accidentally pass an order id where a user id is expected, even though both wrap `String`. ## Zero-cost: boxing avoidance A `value class` holds **exactly one** `val` property in its primary constructor. The `@JvmInline` annotation tells the compiler to **inline** that property at use sites instead of allocating a wrapper object. So `UserId("u-1")` is usually represented at runtime as the bare `String` — no extra heap allocation. This is why it is described as *zero-cost abstraction*: compile-time type safety without runtime overhead. Kotlin still **boxes** (allocates a real wrapper) in specific situations: - the value class is used as a **nullable** (`UserId?`), - it is used **generically** / as a supertype (e.g. stored in `List<Any>`, passed where `Any` is expected), - it is used through an **interface** it implements. In those cases a real object is created, but normal direct usage stays unboxed. ## Capabilities and limits - A value class **can** have properties (computed, no backing field), functions, and `init` blocks, and can implement interfaces. - It **cannot** have `var` backing-field state beyond the single constructor property, cannot extend classes, and the single param must be a `val`. - A `typealias` **cannot** add members, validation, or behavior — it is a name only. You also cannot put an `init`/validation on a `typealias`. ## Choosing - Use **`typealias`** to shorten verbose types and improve readability: `typealias ClickHandler = (View, Int) -> Unit`, or aliasing a deeply generic type. No safety intended. - Use **`@JvmInline value class`** to give a primitive/`String` a domain identity (`Email`, `Money`, `UserId`) and let the compiler enforce it, ideally with validation in `init`.
- Can a typealias prevent passing an order id where a user id is expected if both alias String?No. Both aliases are just String, so they are mutually assignable. Only a value class gives you that compile-time distinction.
- Does using a value class always avoid allocation?No — it is unboxed in direct use, but boxes when used as a nullable, generically, or through an interface.
A typealias is a nickname (still the same person); a value class is putting the value in a labeled, sealed envelope the compiler refuses to open by accident.
saying these in an interview costs you the question
- Claiming a typealias adds type safety or creates a new type
- Saying a value class can wrap multiple constructor properties (it is one val)
- Believing a value class never allocates in any situation
- Confusing @JvmInline value class with the deprecated `inline class`
- Thinking you can add validation/init to a typealias