Advanced Kotlin: Inline, Reified, DSLs & Sealed Types
📖 Concept
Advanced Kotlin features are what separate a senior Kotlin developer from someone who just writes "Java in Kotlin." These features enable type-safe DSLs, zero-overhead abstractions, and exhaustive state modeling.
Inline Functions: The compiler copies the function body to the call site, eliminating the overhead of function calls and lambda object creation. Critical for high-order functions used in hot paths.
Reified Type Parameters:
Only available in inline functions. The type parameter is available at runtime (unlike regular generics which are erased). Enables is T checks, T::class references, and inline function-specific APIs.
DSL Building: Kotlin's extension functions + lambda with receiver + @DslMarker create type-safe domain-specific languages. Used in Compose, Gradle build scripts, Ktor routing, and more.
Sealed Classes/Interfaces:
Model restricted class hierarchies. The compiler knows all subtypes, enabling exhaustive when expressions. Essential for modeling UI state, results, and navigation events.
Value Classes (inline classes): Wrapper types with zero runtime overhead. The compiler removes the wrapper at runtime. Use for type-safe wrappers around primitive types.
💻 Code Example
1// Inline function — eliminates lambda allocation overhead2inline fun <T> measureTime(block: () -> T): Pair<T, Long> {3 val start = System.nanoTime()4 val result = block() // Inlined at call site, no lambda object created5 val duration = (System.nanoTime() - start) / 1_000_0006 return result to duration7}89// Reified — type parameter available at runtime10inline fun <reified T> Intent.getParcelableExtraCompat(key: String): T? {11 return if (Build.VERSION.SDK_INT >= 33) {12 getParcelableExtra(key, T::class.java)13 } else {14 @Suppress("DEPRECATION")15 getParcelableExtra(key) as? T16 }17}1819// Usage: val user = intent.getParcelableExtraCompat<User>("user")20// Without reified, you'd need to pass Class<T> explicitly2122// DSL Building — type-safe builders23@DslMarker24annotation class NetworkDsl2526@NetworkDsl27class RequestBuilder {28 var url: String = ""29 var method: String = "GET"30 private val headers = mutableMapOf<String, String>()31 private var body: String? = null3233 fun headers(block: HeaderBuilder.() -> Unit) {34 HeaderBuilder(headers).apply(block)35 }36 fun body(content: String) { body = content }37 fun build() = Request(url, method, headers, body)38}3940@NetworkDsl41class HeaderBuilder(private val headers: MutableMap<String, String>) {42 infix fun String.to(value: String) { headers[this] = value }43}4445fun request(block: RequestBuilder.() -> Unit): Request {46 return RequestBuilder().apply(block).build()47}4849// Usage — reads like natural language50val req = request {51 url = "https://api.example.com/users"52 method = "POST"53 headers {54 "Authorization" to "Bearer token123"55 "Content-Type" to "application/json"56 }57 body("""{"name": "Alice"}""")58}5960// Sealed interface — exhaustive state modeling61sealed interface NetworkState<out T> {62 data object Idle : NetworkState<Nothing>63 data object Loading : NetworkState<Nothing>64 data class Success<T>(val data: T) : NetworkState<T>65 sealed interface Error : NetworkState<Nothing> {66 data class Network(val message: String) : Error67 data class Server(val code: Int, val message: String) : Error68 data class Auth(val reason: String) : Error69 }70}7172// Exhaustive when — compiler ensures all cases handled73fun <T> handleState(state: NetworkState<T>) = when (state) {74 is NetworkState.Idle -> showIdle()75 is NetworkState.Loading -> showSpinner()76 is NetworkState.Success -> showData(state.data)77 is NetworkState.Error.Network -> showRetry(state.message)78 is NetworkState.Error.Server -> showServerError(state.code)79 is NetworkState.Error.Auth -> navigateToLogin()80 // No 'else' needed — compiler verifies exhaustiveness81}8283// Value class — zero overhead type safety84@JvmInline85value class UserId(val value: String)8687@JvmInline88value class Email(val value: String) {89 init { require(value.contains("@")) { "Invalid email" } }90}9192fun getUser(id: UserId): User { /* ... */ }93// getUser(UserId("123")) ← clear intent94// getUser("123") ← compile error! Type safety without runtime cost
🏋️ Practice Exercise
Practice:
- Create an inline function with a reified type parameter for safe JSON parsing
- Build a type-safe DSL for constructing SQL queries
- Model a complete e-commerce order state machine using sealed interfaces
- Create value classes for all your domain identifiers (UserId, OrderId, ProductId)
- Explain what @DslMarker does and why it's important for nested DSLs
⚠️ Common Mistakes
Overusing inline — only inline functions that take lambdas as parameters or use reified types. Inlining large functions increases bytecode size.
Not using sealed classes for state — using enums or strings for states loses type safety and exhaustive checks
Forgetting that reified only works with inline functions — the type must be known at the call site
Using data class for value semantics when value class suffices — value classes have zero runtime overhead
Not using @DslMarker — without it, nested DSL blocks can access parent scope's methods, creating confusing code
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Advanced Kotlin: Inline, Reified, DSLs & Sealed Types. Login to unlock this feature.