kotlinintermediate
Value Classes — Zero-Cost Wrappers
Create type-safe wrappers with value classes: no runtime overhead, domain identifiers, and units.
kotlinPress ⌘/Ctrl + Shift + C to copy
@JvmInline
value class UserId(val value: Long) {
init {
require(value > 0) { "UserId must be positive" }
}
}
@JvmInline
value class Email(val value: String) {
init {
require(value.contains("@")) { "Invalid email: $value" }
}
val domain: String get() = value.substringAfter("@")
val local: String get() = value.substringBefore("@")
fun normalized() = Email(value.lowercase().trim())
}
@JvmInline
value class Password(private val value: String) {
init {
require(value.length >= 8) { "Password must be at least 8 characters" }
}
// Don't expose in toString!
override fun toString() = "Password(***)"
fun verify(input: String) = value == input
}
// Units of measure
@JvmInline
value class Meters(val value: Double) {
operator fun plus(other: Meters) = Meters(value + other.value)
operator fun minus(other: Meters) = Meters(value - other.value)
operator fun times(scalar: Double) = Meters(value * scalar)
fun toKilometers() = Kilometers(value / 1000.0)
fun toFeet() = Feet(value * 3.28084)
override fun toString() = "${"%.2f".format(value)}m"
}
@JvmInline
value class Kilometers(val value: Double) {
fun toMeters() = Meters(value * 1000.0)
override fun toString() = "${"%.2f".format(value)}km"
}
@JvmInline
value class Feet(val value: Double) {
fun toMeters() = Meters(value / 3.28084)
override fun toString() = "${"%.2f".format(value)}ft"
}
// Money with currency safety
@JvmInline
value class USD(val cents: Long) {
constructor(dollars: Int, cents: Int = 0) : this(dollars * 100L + cents)
val dollars get() = cents / 100
val remainingCents get() = cents % 100
operator fun plus(other: USD) = USD(cents + other.cents)
operator fun minus(other: USD) = USD(cents - other.cents)
operator fun times(quantity: Int) = USD(cents * quantity)
override fun toString() = "\$${dollars}.${remainingCents.toString().padStart(2, '0')}"
}
// Type-safe IDs prevent mixing
@JvmInline
value class OrderId(val value: String)
@JvmInline
value class ProductId(val value: String)
data class OrderItem(
val orderId: OrderId,
val productId: ProductId,
val quantity: Int,
val price: USD
)
// Function that won't accept wrong ID type
fun findOrder(id: OrderId): String = "Order: ${id.value}"
fun findProduct(id: ProductId): String = "Product: ${id.value}"
// findOrder(ProductId("p-1")) ← compile error!
fun main() {
// Domain IDs
val userId = UserId(42)
val email = Email("Alice@Example.com")
val password = Password("secret123")
println("User: $userId")
println("Email: ${email.normalized().value}")
println("Domain: ${email.domain}")
println("Password: $password") // Password(***)
println("Verify: ${password.verify("secret123")}")
// Units
val distance = Meters(1500.0)
println("\nDistance: $distance")
println("In km: ${distance.toKilometers()}")
println("In feet: ${distance.toFeet()}")
println("Double: ${distance * 2.0}")
// Money
val price = USD(29, 99)
val tax = USD(2, 40)
val total = price + tax
println("\nPrice: $price")
println("Tax: $tax")
println("Total: $total")
println("3x: ${price * 3}")
// Type-safe IDs
val orderId = OrderId("ord-123")
val productId = ProductId("prod-456")
println("\n${findOrder(orderId)}")
println(findProduct(productId))
val item = OrderItem(orderId, productId, 2, price)
println("Item: $item")
}Use Cases
- Type-safe domain identifiers
- Units of measure without overhead
- Preventing primitive type confusion
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinbeginner
Value Classes and Type Aliases
Use value classes for type-safe wrappers without runtime overhead and type aliases for readability.
Best for: Type-safe domain primitives without runtime cost
#kotlin#value-class
kotlinintermediate
Sealed Classes and Exhaustive when
Model restricted hierarchies with sealed classes: exhaustive when, data objects, and state machines.
Best for: Type-safe API response handling
#kotlin#sealed-class
kotlinadvanced
Inline Functions and Reified Generics
Use inline functions for zero-overhead abstractions: reified type parameters, crossinline, and noinline.
Best for: Type-safe parsing and casting utilities
#kotlin#inline
kotlinadvanced
Generics — Variance, Bounds, and Type Projections
Master Kotlin generics: in/out variance, upper bounds, type projections, and generic constraints.
Best for: Type-safe generic APIs and containers
#kotlin#generics