In Kotlin, sealed classes are a powerful feature that allow you to define restricted class hierarchies. Unlike regular classes, which can be inherited from anywhere in the codebase, sealed classes restrict their subclasses to be defined within the same file as the sealed class itself. This restriction is particularly useful for creating finite state machines or when you want to enforce certain constraints on your class hierarchy.
A sealed class is declared using the sealed keyword. The primary purpose of a sealed class is to ensure that all its subclasses are confined to the same file, making it easier to manage and reason about the class hierarchy. Sealed classes can have abstract methods, constructors, and properties just like regular classes.
Here are some key points about sealed classes:
when expression for exhaustive pattern matching.Let's dive into some examples to understand how sealed classes work and how they can be used in practice.
Consider a scenario where you want to represent different states of an order in an e-commerce application. You can use a sealed class to define these states:
1sealed class OrderState {2object Pending : OrderState()3object Shipped : OrderState()4data class Delivered(val deliveryDate: String) : OrderState()5data class Cancelled(val reason: String) : OrderState()6}78fun handleOrder(state: OrderState) {9when (state) {10is OrderState.Pending -> println("Order is pending.")11is OrderState.Shipped -> println("Order has been shipped.")12is OrderState.Delivered -> println("Order delivered on ${state.deliveryDate}.")13is OrderState.Cancelled -> println("Order cancelled due to ${state.reason}.")14}15}1617fun main() {18val order = OrderState.Delivered("2023-10-01")19handleOrder(order)20}
In this example, OrderState is a sealed class with four subclasses: Pending, Shipped, Delivered, and Cancelled. Each subclass represents a different state of an order. The handleOrder function uses the when expression to perform exhaustive pattern matching on the OrderState.
Let's consider another practical example where sealed classes can be used to represent different types of shapes in a graphics application:
1sealed class Shape {2abstract fun area(): Double3}45data class Circle(val radius: Double) : Shape() {6override fun area(): Double = Math.PI * radius * radius7}89data class Rectangle(val width: Double, val height: Double) : Shape() {10override fun area(): Double = width * height11}1213fun printArea(shape: Shape) {14println("The area of the shape is ${shape.area()}")15}1617fun main() {18val circle = Circle(5.0)19val rectangle = Rectangle(3.0, 4.0)2021printArea(circle)22printArea(rectangle)23}
In this example, Shape is a sealed class with two subclasses: Circle and Rectangle. Each subclass implements the area() method to calculate its area. The printArea function takes a Shape object and prints its area.
In the next section, we will explore companion objects in Kotlin, which are static members of a class that can be used to define utility functions or properties related to the class.
By understanding sealed classes and their use cases, you can write more robust and maintainable code by restricting inheritance and leveraging exhaustive pattern matching.