Sealed classes are a unique and powerful feature in Kotlin that allows you to define a restricted hierarchy of classes where each class represents a specific type or state. Sealed classes are particularly useful when you want to create a limited set of subclasses, often used for modeling finite states or as an alternative to enums. In this guide, we’ll explore sealed classes in Kotlin, their syntax, advantages, and practical use cases.
Defining a Sealed Class
To declare a sealed class in Kotlin, you use the sealed
keyword followed by the class declaration. Inside a sealed class, you define a restricted set of subclasses. Here’s the basic syntax of a sealed class:
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}
In this example, we’ve defined a sealed class Result
with two subclasses, Success
and Error
. The sealed class restricts the set of subclasses to only those defined within it.
Restricted Hierarchy
Sealed classes enforce a restricted hierarchy, meaning that the subclasses of a sealed class must be declared within the same file where the sealed class is defined. This restriction ensures that the hierarchy is limited and well-defined.
Instances of Subclasses
Each subclass of a sealed class represents a specific type or state. In the example above, Success
and Error
are subclasses of Result
, and each one carries specific data. You can create instances of these subclasses like regular classes:
val successResult = Result.Success("Data loaded successfully") val errorResult = Result.Error("Failed to load data")
Each instance carries its own data, making it easy to represent different outcomes or states in your code.
When Expressions with Sealed Classes
One of the most significant advantages of sealed classes is their compatibility with Kotlin’s when
expressions. A when
expression allows you to handle different cases or states explicitly, providing a clean and concise way to work with sealed class instances:
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
}
}
In this example, the handleResult()
function takes a Result
as an argument and uses a when
expression to handle the different cases of Result
. Because Result
is a sealed class, the compiler ensures that all possible cases are covered, making your code more robust.
Advantages of Sealed Classes
Sealed classes offer several advantages in Kotlin:
- Restricted Hierarchy: Sealed classes restrict the set of possible subclasses, providing a clear and limited hierarchy of classes. This is particularly useful when modeling finite states or representing a closed set of options.
- Explicit Handling: When used in
when
expressions, sealed classes make it clear which cases must be handled. The compiler ensures that all possible cases are considered, reducing the likelihood of runtime errors. - Data and State Modeling: Sealed classes are ideal for modeling data with different states or outcomes, such as network requests, validation results, or state machines.
- Code Readability: Sealed classes make code more self-documenting by explicitly defining the possible states or types an object can have.
- Pattern Matching: Sealed classes, in combination with
when
expressions, enable pattern matching, a powerful feature for working with complex data structures.
Practical Use Cases
Sealed classes are commonly used in various scenarios, including:
- Network Responses: When making network requests, sealed classes can represent different response states like success, error, or loading.
- Validation Results: Sealed classes can model the results of validation processes, distinguishing between valid and invalid states.
- State Machines: Sealed classes can represent states and transitions in state machines, making the code more structured and readable.
- Finite Sets of Options: When you have a fixed set of options or choices, sealed classes can represent each option as a subclass, ensuring that only valid choices are available.
Command and Example
Here’s a complete example demonstrating the use of sealed classes in Kotlin:
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
}
}
fun main() {
val successResult = Result.Success("Data loaded successfully")
val errorResult = Result.Error("Failed to load data")
handleResult(successResult)
handleResult(errorResult)
}
In this example, we’ve defined a sealed class Result
with two subclasses, Success
and Error
. The handleResult()
function uses a when
expression to handle different cases of Result
. In the main()
function, we create instances of both Success
and Error
and pass them to handleResult()
, demonstrating how sealed classes provide a structured and safe way to work with different states or outcomes.