Type System

Kotlin's type system — data classes, sealed classes, enums, inline/value classes, generics, and type inference

Kotlin’s type system combines safety and expressiveness. Here are the key constructs.

Data classes

Auto-generates equals, hashCode, toString, copy, and componentN functions.

data class User(val name: String, val age: Int)

val user = User("Alice", 30)
user.name                                     // "Alice"
user.toString()                               // "User(name=Alice, age=30)"
user.copy(age = 31)                           // User(name=Alice, age=31)

// Destructuring
val (name, age) = user                        // name="Alice", age=30

// Data classes work in when expressions and collections
val set = setOf(User("Alice", 30))            // deduplication by equals/hashCode

Sealed classes and interfaces

Restricted class hierarchies — the compiler knows all subtypes. Perfect for when exhaustiveness.

sealed class Result<out T> {
    data class Success<T>(val value: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

fun handle(result: Result<Int>) = when (result) {
    is Result.Success -> println("Got ${result.value}")
    is Result.Error -> println("Error: ${result.message}")
    Result.Loading -> println("Loading...")
    // no else needed — compiler knows all cases
}

// Sealed interfaces (1.5+)
sealed interface Node {
    data class Leaf(val value: Int) : Node
    data class Branch(val left: Node, val right: Node) : Node
}

Enum classes

enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

// With properties and methods
enum class Planet(val mass: Double, val radius: Double) {
    EARTH(5.97e24, 6371.0),
    MARS(6.42e23, 3389.5),
    JUPITER(1.90e27, 69911.0);

    fun surfaceGravity() = 6.674e-11 * mass / (radius * radius * 1000 * 1000)
}

// Using enums
val dir = Direction.NORTH
dir.name                                     // "NORTH"
dir.ordinal                                  // 0
Direction.valueOf("SOUTH")                   // Direction.SOUTH
Direction.entries                            // all entries

// In when expressions
fun describe(dir: Direction) = when (dir) {
    Direction.NORTH -> "Up"
    Direction.SOUTH -> "Down"
    Direction.EAST -> "Right"
    Direction.WEST -> "Left"
}

Inline / value classes

Wrap a single value without allocation overhead. Useful for type safety.

@JvmInline
value class Password(val value: String)      // no runtime allocation

@JvmInline
value class UserId(val id: Long)             // type-safe at compile time, Long at runtime

fun findUser(id: UserId) { /* ... */ }
findUser(UserId(42L))                        // ok
// findUser(42L)                             // compile error — wrong type

Type aliases

typealias UserMap = Map<String, List<User>>
typealias Predicate<T> = (T) -> Boolean

val users: UserMap = mapOf("team1" to listOf(User("Alice", 30)))
val isAdult: Predicate<User> = { it.age >= 18 }

Generics

// Generic class
class Box<T>(val value: T)

val intBox = Box(42)                         // Box<Int>
val stringBox = Box("hello")                 // Box<String>

// Generic function
fun <T> List<T>.second(): T = this[1]

// Variance
// Declaration-site: out = covariant (producer), in = contravariant (consumer)
interface Source<out T> {                     // T is covariant
    fun next(): T
}

interface Sink<in T> {                       // T is contravariant
    fun put(value: T)
}

// Use-site variance (like Java wildcards)
fun copy(from: Array<out Any>, to: Array<in Any>) {
    from.forEachIndexed { i, v -> to[i] = v }
}

// Reified type parameters — available at runtime (inline only)
inline fun <reified T> isA(value: Any): Boolean = value is T

isA<String>("hello")                         // true
isA<Int>("hello")                            // false

Object expressions and declarations

// Singleton object
object Database {
    val url = "jdbc:localhost:5432/mydb"
    fun connect() { /* ... */ }
}
Database.connect()

// Companion object — like static in Java
class MyClass {
    companion object {
        fun create(): MyClass = MyClass()
        const val TAG = "MyClass"
    }
}
val instance = MyClass.create()

// Anonymous object (like Java anonymous inner class)
val listener = object : ClickListener {
    override fun onClick() { /* ... */ }
}

Extension functions

// Add methods to existing types
fun String.isEmail(): Boolean = this.contains("@") && this.contains(".")

fun Int.square(): Int = this * this

// Use
"[email protected]".isEmail()                 // true
5.square()                                    // 25

// Extension on nullable
fun String?.isNullOrEmpty(): Boolean = this == null || this.isEmpty()

// Generic extension
fun <T> List<T>.secondOrNull(): T? =
    if (size >= 2) this[1] else null