Sealed classes are an important feature in the Kotlin language that allows you to define restricted class hierarchies. A sealed class can be subclassed, but all subclasses must be defined within the same file as the sealed class itself. This provides a compile-time guarantee that all possible subclasses of the sealed class are known.
Let's say we want to model different shapes in our application. We can use a sealed class to define the possible shapes and their properties:
sealed class Shape {
abstract fun calculateArea(): Double
}
data class Circle(val radius: Double) : Shape() {
override fun calculateArea() = Math.PI * radius * radius
}
data class Rectangle(val width: Double, val height: Double) : Shape() {
override fun calculateArea() = width * height
}
In the example above, the sealed class Shape is defined with two subclasses: Circle and Rectangle. Each subclass overrides the calculateArea method to provide the specific implementation based on its properties. Note that the subclasses are defined as data classes for convenience.
One of the main benefits of sealed classes is the ability to use pattern matching when working with them. Since all subclasses of a sealed class are known, we can use a when expression to handle each subclass differently:
fun printShapeArea(shape: Shape) {
val area = when (shape) {
is Circle -> "The circle's area is ${shape.calculateArea()}"
is Rectangle -> "The rectangle's area is ${shape.calculateArea()}"
}
println(area)
}
val circle = Circle(5.0)
val rectangle = Rectangle(3.0, 4.0)
printShapeArea(circle) // Output: "The circle's area is 78.53981633974483"
printShapeArea(rectangle) // Output: "The rectangle's area is 12.0"
In the code snippet above, the printShapeArea function takes a parameter of type Shape and uses a when expression to match the shape's type. This allows us to provide different behavior depending on the actual subtype of the sealed class at runtime.
Sealed classes in Kotlin are a powerful tool for defining restricted class hierarchies. They ensure that all possible subclasses are known at compile-time and enable pattern matching to handle each subclass differently. Sealed classes are particularly useful when modeling finite state machines, algebraic data types, or other cases where the set of subclasses is limited.