Sealed Class in Kotlin
- Introduction to Sealed Class
- A sealed class is a class that can only be subclassed within the same file where it’s declared
- Subclasses must be declared within the same file as the sealed class
- Sealed classes are often used to represent a closed set of classes, such as when defining the different states of a state machine
2. Sealed Class vs. Enum Class
- Sealed classes are similar to enum classes, but provide more flexibility
- Enum classes represent a fixed set of values, whereas sealed classes can represent a fixed set of classes with additional data and behavior
3. Creating a Sealed Class
- To create a sealed class, use the “sealed” keyword before the class declaration
- Sealed classes can have subclasses declared as normal classes within the same file
Example:
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: Throwable) : Result()
}
4. Using Sealed Class in When Expressions
- One of the primary uses of sealed classes is with “when” expressions to handle different cases
- Since sealed classes provide a closed set of subclasses, “when” expressions can be exhaustive and the compiler can ensure all cases are covered
Example:
fun handleResult(result: Result) {
when (result) {
is Result.Success -> {
// handle success case
}
is Result.Failure -> {
// handle failure case
}
}
}
5. Sealed Class with Generics
- Sealed classes can also be used with generics to provide additional flexibility
- Sealed classes can also use generics to allow for flexible type parameters in the subclasses
sealed class Result<T> {
data class Success<T>(val data: T) : Result<T>()
data class Failure<T>(val error: Throwable) : Result<T>()
}
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Failure(val error: Throwable) : Result<Nothing>()
fun handle(onSuccess: (T) -> Unit, onFailure: (Throwable) -> Unit) {
when (this) {
is Success -> onSuccess(data)
is Failure -> onFailure(error)
}
}
}
6. Sealed Class with Inheritance
- Sealed classes can also inherit from other sealed classes or regular classes, but all subclasses must still be declared in the same file
sealed class Animal {
sealed class Mammal : Animal() {
data class Cat(val name: String) : Mammal()
data class Dog(val name: String) : Mammal()
}
sealed class Reptile : Animal() {
data class Snake(val length: Double) : Reptile()
data class Lizard(val hasTail: Boolean) : Reptile()
}
}
7. Sealed Class with Common Properties
- Subclasses of a sealed class can have common properties, which can be accessed from the sealed class
Example:
sealed class Shape {
abstract val name: String
data class Circle(override val name: String, val radius: Double) : Shape()
data class Rectangle(override val name: String, val width: Double, val height: Double) : Shape()
fun describe() {
println("$name has an area of ${calculateArea()}")
}
private fun calculateArea(): Double {
return when (this) {
is Circle -> Math.PI * radius * radius
is Rectangle -> width * height
}
}
}
8. Sealed Class with Interfaces
- Sealed classes can also implement interfaces, which can provide additional behavior
Example:
sealed class Result : Serializable {
data class Success(val data: String) : Result(), Serializable
data class Failure(val error: Throwable) : Result(), Serializable
fun log() {
when (this) {
is Success -> println("Success: $data")
is Failure -> println("Error: ${error.message}")
}
}
}
9. Sealed Class with Companion Object
- Sealed classes can also have companion objects, which can provide additional utility methods or constants
Example:
sealed class Color {
data class Red(val value: Int) : Color()
data class Green(val value: Int) : Color()
data class Blue(val value: Int) : Color()
companion object {
val BLACK = Color.Red(0) // we are reusing the Red class for BLACK
val WHITE = Color.Blue(255) // we are reusing the Blue class for WHITE
}
}
10. Sealed Class with Nested Sealed Class
- Sealed classes can also have nested sealed classes, which can provide additional levels of abstraction or hierarchy
Example:
sealed class Vehicle {
sealed class Car : Vehicle() {
data class Sedan(val model: String) : Car()
data class SUV(val model: String) : Car()
}
sealed class Bike : Vehicle() {
data class Cruiser(val model: String) : Bike()
data class Sport(val model: String) : Bike()
}
}
In summary, sealed classes provide a powerful way to represent a closed set of classes in Kotlin. They can be used to simplify code and make “when” expressions exhaustive.