Easy Tutorial
❮ Kotlin Enum Classes Home ❯

Kotlin Data Classes and Sealed Classes


Data Classes

Kotlin can create a class that contains only data, with the keyword data:

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

The compiler automatically extracts the following functions from the primary constructor based on all declared properties:

If these functions are explicitly defined in the class or inherited from a superclass, they will not be generated.

To ensure consistency and meaningfulness of the generated code, data classes must meet the following conditions:

-

The primary constructor must contain at least one parameter.

-

All parameters of the primary constructor must be marked as val or var;

-

Data classes cannot be declared as abstract, open, sealed, or inner;

-

Data classes cannot inherit from other classes (but can implement interfaces).

Copying

Copying is done using the copy() function, which allows us to copy an object and modify some properties. For the aforementioned User class, its implementation would look something like this:

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

Example

Using the copy function to replicate the User data class and modify the age property:

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

fun main(args: Array<String>) {
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)
    println(jack)
    println(olderJack)
}

The output will be:

User(name=Jack, age=1)
User(name=Jack, age=2)

Data Classes and Destructuring Declarations

Component functions allow data classes to be used in destructuring declarations:

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

Standard Data Classes

The standard library provides Pair and Triple. In most cases, naming data classes is a better design choice because it makes the code more readable and provides meaningful names and properties.


Sealed Classes

Sealed classes are used to represent restricted class inheritance structures: when a value is one of a limited number of types and cannot be any other type. In a sense, they are an extension of enum classes: the set of values for an enum type is also limited, but each enum constant only exists as a single instance, whereas a subclass of a sealed class can have multiple instances that may contain state.

To declare a sealed class, use the sealed modifier on the class. Sealed classes can have subclasses, but all subclasses must be nested within the sealed class.

sealed cannot be used to modify interfaces, abstract classes (this will result in a warning, but no compilation error)

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

The key benefit of using sealed classes is that when using a when expression, if it can be verified that the statement covers all cases, there is no need to add an else clause for the statement.

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // No need for an `else` clause, as we have covered all cases
}
❮ Kotlin Enum Classes Home ❯