skip to content

typealias vs Inline value class

A typealias is transparent and offers no protection, while a @JvmInline value class is a distinct type the compiler enforces yet usually erases to its underlying value. Interviewers pose the UserId-versus-String scenario to see whether you pick the one that actually prevents the mix-up.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between a `typealias` and a `@JvmInline value class` in Kotlin, and when would you choose each?

level: juniorimportance: must knowfreq 70%

answer

  1. typealias = transparent synonym, no new type
  2. value class = distinct wrapper, real type safety
  3. @JvmInline inlines the single val → no allocation
  4. boxes when nullable / generic / behind interface
  5. alias for readability, value class for domain identity

basics

~20 s

A 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 lines
kotlin
typealias 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 explicitly

go deeper

for a junior

States the core distinction: typealias is interchangeable, value class is a separate type the compiler keeps distinct.

for a middle

Adds that @JvmInline inlines the single property to avoid allocation and gives concrete use-case choices.

for a senior

Explains exactly when a value class still boxes (nullable/generic/interface) and the single-val/no-var constraints.

for a principal

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

context

open as a page

A teammate uses `typealias Email = String` and `typealias Phone = String` to make signatures clearer. What problem can this hide, and how would a value class fix it?

level: middleimportance: must knowfreq 55%

basics

~20 s

Because both aliases are just String, the compiler treats them as the same type. You can accidentally pass a phone number where an email is expected and nothing complains. A value class makes them separate types, so the compiler catches the mix-up.

open as a page

What members and behavior can a `@JvmInline value class` have that a `typealias` cannot, and what are the value class's structural constraints?

level: middleimportance: should knowfreq 40%

basics

~20 s

A value class is a real type, so it can have methods, computed properties, validation, and implement interfaces. A typealias is only a name and can hold none of those. The value class must wrap exactly one read-only value and can't inherit from a class.

open as a page

When does a `@JvmInline value class` get boxed at runtime instead of being inlined, and why does that matter?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Normally the value class is replaced by its single inner value, so no extra object is created. But when it must behave like a real object — for example when it is nullable, stored as a general type, or used through an interface — the compiler creates a wrapper object.

open as a page

From a library/API-design perspective, what are the trade-offs of exposing a `typealias` versus a `@JvmInline value class` in a public API, especially regarding binary compatibility and Java interop?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

A typealias is just a name that disappears, so callers really depend on the underlying type; you can't truly change it later without breaking them. A value class is a real, safer type but adds wrapping, possible boxing, and trickier Java interop because Kotlin renames its methods.

open as a page