Sling Academy
Home/Kotlin/Using Sealed Classes for Restricted Class Hierarchies in Kotlin

Using Sealed Classes for Restricted Class Hierarchies in Kotlin

Last updated: December 05, 2024

Sealed classes are an integral feature of Kotlin that bring numerous advantages to modeling restricted class hierarchies. They are a powerful tool for domain modeling, allowing only a finite set of subclasses and preventing the creation of outside subclasses.

In conventional programming paradigms, handling exhaustive logic based on the possible states of a system can be cumbersome. With sealed classes, you gain the safety of exhaustive when expressions, enabling compiler checks for all possible cases. This is especially useful in Kotlin, where immutability and type safety are key design principles. Let's explore the mechanics and benefits of sealed classes through practical examples.

Defining Sealed Classes

To define a sealed class in Kotlin, use the sealed keyword before the class keyword. The subclasses of a sealed class must be defined within the same source file, although they can be in different classes or within the sealed class itself.


sealed class PaymentMethod {
   object Cash : PaymentMethod()
   data class CreditCard(val number: String) : PaymentMethod()
   data class PayPal(val email: String) : PaymentMethod()
}

In this example, PaymentMethod represents a restricted class hierarchy with three subclasses: Cash, CreditCard, and PayPal. This enforces that only these three types can be used as a payment method.

Advantages of Using Sealed Classes

One significant advantage of using sealed classes over regular inheritance in Kotlin is the ability to exhaustively iterate over every subclass using a when expression. This guarantees that you handle every possible case or an explicit default, leading to safer and self-documenting code.


fun processPayment(payment: PaymentMethod) = when (payment) {
    is PaymentMethod.Cash -> println("Processing cash payment")
    is PaymentMethod.CreditCard -> println("Processing credit card payment")
    is PaymentMethod.PayPal -> println("Processing PayPal payment")
    // No need for an "else" clause since all cases are covered
}

This exhaustive pattern matching at compile time assists developers in anticipating all variations of subclasses, reducing the likelihood of runtime exceptions caused by unhandled cases.

Sealed Classes vs. Enum Classes

You might wonder whether to choose sealed classes or enum classes to represent a restricted set of hierarchies. While both offer similar properties by providing constraints, sealed classes are more flexible. They allow each type to have its own state and behavior, unlike enums, which only offer a finite set of constant values.


sealed class NetworkResponse {
    data class Success(val data: String) : NetworkResponse()
    data class Error(val error: Throwable) : NetworkResponse()
    object Timeout : NetworkResponse()
}

We'll notice here how sealed classes allow modeling complex types where constants can have their own meanings and properties. This cannot be easily mimicked using enum classes.

Restrictions and Considerations

While sealed classes provide a lot of benefits, they do come with certain restrictions:

  • All implementations and subclasses of a sealed class must reside in the same file.
  • Sealed classes cannot be instantiated directly; you can only work with their subclasses.
  • They do not provide serialization by default. Additional work is necessary if you need to serialize objects from sealed classes.

Conclusion

Sealed classes, when used appropriately, offer a robust means to express restricted class hierarchies, facilitating safer and more maintainable Kotlin applications. They neatly fill the gap between data integrity checks and hierarchies where you would need exhaustive handling. As you explore deeper into Kotlin and need custom classes to tightly control inherited types, Kotlin’s sealed classes will undoubtedly become an invaluable part of your toolkit.

Next Article: Practical Use Cases for Sealed Classes in Kotlin

Previous Article: What Are Sealed Classes in Kotlin?

Series: Kotlin Object-Oriented Programming

Kotlin

You May Also Like

  • How to Use Modulo for Cyclic Arithmetic in Kotlin
  • Kotlin: Infinite Loop Detected in Code
  • Fixing Kotlin Error: Index Out of Bounds in List Access
  • Setting Up JDBC in a Kotlin Application
  • Creating a File Explorer App with Kotlin
  • How to Work with APIs in Kotlin
  • What is the `when` Expression in Kotlin?
  • Writing a Script to Rename Multiple Files Programmatically in Kotlin
  • Using Safe Calls (`?.`) to Avoid NullPointerExceptions in Kotlin
  • Chaining Safe Calls for Complex Operations in Kotlin
  • Using the Elvis Operator for Default Values in Kotlin
  • Combining Safe Calls and the Elvis Operator in Kotlin
  • When to Avoid the Null Assertion Operator (`!!`) in Kotlin
  • How to Check for Null Values with `if` Statements in Kotlin
  • Using `let` with Nullable Variables for Scoped Operations in Kotlin
  • Kotlin: How to Handle Nulls in Function Parameters
  • Returning Nullable Values from Functions in Kotlin
  • Safely Accessing Properties of Nullable Objects in Kotlin
  • How to Use `is` for Nullable Type Checking in Kotlin