Kotlin Sealed class

Kotlin sealed classes provide a powerful mechanism for defining restricted hierarchies where a value can only have one of the types from a limited set. This article dives into sealed classes, their purpose, usage, benefits, and a real-world example showcasing their functionality.

Understanding Sealed Classes

Sealed classes in Kotlin are used to represent restricted hierarchies where a value can have a limited number of types. Unlike regular classes, sealed classes can have a predefined set of subclasses within the same file, and they are often used to model complex data structures with a finite number of possibilities.

Example Problem

Let’s consider a scenario where we have different shapes such as Circle, Square, and Rectangle. We want to calculate the area of each shape, but we also want to handle any new shapes added in the future without introducing bugs.

Creating a Sealed Class

To address the above problem, we’ll create a sealed class Shape with subclasses representing different shapes.

sealed class Shape

class Circle(val radius: Double) : Shape()
class Square(val sideLength: Double) : Shape()
class Rectangle(val length: Double, val width: Double) : Shape()

fun calculateArea(shape: Shape): Double {
    return when (shape) {
        is Circle -> Math.PI * shape.radius * shape.radius
        is Square -> shape.sideLength * shape.sideLength
        is Rectangle -> shape.length * shape.width
    }
}

In this example:

  • Shape is a sealed class representing different shapes.
  • Circle, Square, and Rectangle are subclasses of Shape, each representing a specific shape.

Let’s use the sealed class Shape in a real-world scenario where we calculate the areas of different shapes.

fun main() {
    val circle = Circle(5.0)
    val square = Square(4.0)
    val rectangle = Rectangle(3.0, 6.0)

    println("Area of Circle: ${calculateArea(circle)}")
    println("Area of Square: ${calculateArea(square)}")
    println("Area of Rectangle: ${calculateArea(rectangle)}")
}

Output:

Area of Circle: 78.53981633974483
Area of Square: 16.0
Area of Rectangle: 18.0

Explanation

  • We create instances of Circle, Square, and Rectangle, passing relevant parameters.
  • The calculateArea function calculates the area based on the type of shape using a when expression.

Example: Payment Methods Management

Imagine you’re developing a payment processing system that supports multiple payment methods such as Credit Card, PayPal, and Bank Transfer. Each payment method has its specific details and processing logic. Sealed classes can help model these payment methods effectively.

Sealed Class: PaymentMethod

Let’s create a sealed class PaymentMethod with subclasses representing different payment methods.

sealed class PaymentMethod {
    abstract fun processPayment(amount: Double): Boolean
}

class CreditCard(val cardNumber: String, val cvv: String) : PaymentMethod() {
    override fun processPayment(amount: Double): Boolean {
        // Process credit card payment logic
        println("Processing credit card payment of $amount")
        return true
    }
}

class PayPal(val email: String, val password: String) : PaymentMethod() {
    override fun processPayment(amount: Double): Boolean {
        // Process PayPal payment logic
        println("Processing PayPal payment of $amount")
        return true
    }
}

class BankTransfer(val accountNumber: String) : PaymentMethod() {
    override fun processPayment(amount: Double): Boolean {
        // Process bank transfer payment logic
        println("Processing bank transfer payment of $amount")
        return true
    }
}

In this example:

  • PaymentMethod is a sealed class representing different payment methods.
  • CreditCard, PayPal, and BankTransfer are subclasses of PaymentMethod, each representing a specific payment method.

Payment Processing

Let’s use the sealed class PaymentMethod to process payments in our system.

fun main() {
    val creditCard = CreditCard("1234 5678 9012 3456", "123")
    val payPal = PayPal("[email protected]", "password")
    val bankTransfer = BankTransfer("0123456789")

    processPayment(creditCard, 100.0)
    processPayment(payPal, 50.0)
    processPayment(bankTransfer, 200.0)
}

Output:

Processing credit card payment of 100.0
Payment successful!
Processing PayPal payment of 50.0
Payment successful!
Processing bank transfer payment of 200.0
Payment successful!

Explanation

  • We create instances of CreditCard, PayPal, and BankTransfer, representing different payment methods.
  • The processPayment function takes a PaymentMethod and an amount, processes the payment using the specific payment method’s logic, and prints the payment status.

In this real-world example, Kotlin sealed classes provide a structured and type-safe way to handle various payment methods in a payment processing system, ensuring each payment method’s specific logic is correctly implemented and used.

Benefits of Sealed Classes

  1. Compiler Checks: Sealed classes restrict the hierarchy, ensuring that all subclasses are handled explicitly.
  2. Enhanced Readability: By using sealed classes, code becomes more readable and maintainable, especially in scenarios with restricted hierarchies.
Kotlin sealed classes are a powerful tool for managing restricted hierarchies, such as handling different types of shapes, states, or events. By defining a sealed class, developers can ensure exhaustive handling of all possible subclasses, reducing the risk of runtime errors and improving code robustness.