What members and behavior can a `@JvmInline value class` have that a `typealias` cannot, and what are the value class's structural constraints?
answer
- Alias = name only: no members, no validation
- Value class: methods, computed props, init require, interfaces
- Exactly one constructor param, must be val
- No class inheritance; interfaces only; implicitly final
- @JvmInline required on the JVM
basics
~20 sA 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.
solid answer
~50 sA `typealias` is purely a name — it carries **no members, no validation, no behavior**; it is erased to the aliased type. A `@JvmInline value class` is a genuine type, so it can declare **functions**, **computed properties** (no backing field), an **`init { require(...) }`** validation block, secondary constructors with bodies, and it can **implement interfaces**. Its structural constraints: the **primary constructor must have exactly one parameter and it must be a `val`** (not `var`); the underlying type cannot be the value class itself; it **cannot extend** another class (no inheritance, it is implicitly final) — only interface implementation is allowed; it cannot have additional state with backing fields beyond that single property; and it cannot be a `data` class with extra fields. It must be annotated `@JvmInline` on the JVM. These constraints are what let the compiler inline the single value while still exposing real behavior — capabilities a transparent alias fundamentally can't offer.
code
kotlin · 8 lines@JvmInline
value class Email(val value: String) {
init { require('@' in value) } // validation
val domain: String get() = value.substringAfter('@') // computed, no backing field
fun isFrom(host: String) = domain == host
}
// typealias cannot host any of the above:
typealias EmailAlias = String // just a namego deeper
Knows a value class can have methods while an alias is just a name.
Enumerates allowed members (init, computed props, interfaces) and the one-val constraint.
Explains why the constraints (single immutable value, no inheritance) are required for inlining and how interface use forces boxing.
Reasons about API design with constrained value types and the immutability/equality guarantees the constraints buy.
## typealias: a name only ```kotlin typealias Predicate<T> = (T) -> Boolean ``` A `typealias` cannot: - declare functions, properties, or an `init` block; - add validation or invariants; - implement interfaces or change the type's behavior. It is **erased** to the aliased type; it exists only for readability/abbreviation. ## value class: a real (but constrained) type ```kotlin @JvmInline value class Percent(val raw: Int) : Comparable<Percent> { init { require(raw in 0..100) { "out of range: $raw" } } // validation val fraction: Double get() = raw / 100.0 // computed prop, no backing field fun clampAdd(other: Percent) = Percent((raw + other.raw).coerceAtMost(100)) override fun compareTo(other: Percent) = raw.compareTo(other.raw) // implements interface } ``` **Allowed members:** - member **functions**; - **computed properties** (must have no backing field — i.e., custom getters); - an **`init`** block for validation (`require`/`check`); - **secondary constructors** (since Kotlin 1.x they may even have bodies in recent versions); - **interface implementation** (e.g., `Comparable`). **Structural constraints (what makes it a value class):** - **Exactly one primary-constructor parameter**, and it must be a **`val`** (read-only). No `var`, no second param. - **No backing-field state** beyond that single property — extra properties must be computed. - **No class inheritance**: a value class cannot have a superclass and is effectively final; it can only **implement interfaces**. - The underlying type **cannot be the value class itself** (no self-recursion). - On the JVM it must be marked **`@JvmInline`** (the bare `value class` keyword reserves the concept; `@JvmInline` selects the inlined representation). - Equality/hashCode are derived structurally from the single value. ## Why the constraints exist Inlining works by replacing the wrapper with its single underlying value at runtime. That is only possible if there is exactly **one** piece of real state and no inherited object identity to preserve. The constraints are precisely what keep the abstraction *zero-cost* while still letting it expose methods and validation — the decisive advantage over a `typealias`, which can express none of this. ## Quick comparison | Capability | typealias | @JvmInline value class | |---|---|---| | New distinct type | No | Yes | | Methods / computed props | No | Yes | | init validation | No | Yes | | Implement interface | No | Yes | | Extend a class | n/a | No (final, interfaces only) | | Runtime cost | none (erased) | none in direct use; boxes sometimes |
- Can a value class implement an interface, and what is the runtime consequence?Yes. But when it is used *through* that interface, it must be boxed into a real object, so you lose the unboxed representation there.
- Why must the single constructor parameter be a val and not a var?Value classes are immutable so the compiler can safely represent them by their underlying value; a mutable var would break that representation and equality.
saying these in an interview costs you the question
- Thinking a typealias can carry methods or validation
- Claiming a value class can have multiple stored properties
- Saying a value class can extend another class
- Adding a backing-field property beyond the single val
- Forgetting @JvmInline is required on the JVM