Sealed Class

Introduction

Sealed classes are a powerful feature in Kotlin that help developers define restricted class hierarchies. Imagine you have a situation where you only want to allow a specific set of types, and you want to ensure that no other types can be added outside of this set. Sealed classes provide a clean and safe way to achieve this.

Why Sealed Classes Matter

When working with complex data models, especially in scenarios like state management or representing different types of responses, it’s crucial to maintain type safety. Sealed classes offer a way to define a closed set of subclasses, making your code safer and easier to manage. You’ll often see them used in conjunction with when expressions, allowing you to handle each case explicitly, while also ensuring that all potential cases are accounted for.

Concept Explanation

A sealed class serves as a base class that restricts its subclasses. All subclasses must be defined within the same file as the sealed class itself. This limitation ensures that you know all possible subclasses at compile time, which is particularly useful for exhaustive checks in when expressions.

Analogy

Think of a sealed class like a well-organized toolbox. If you have a toolbox for a specific task, you can only add tools that are relevant to that task. Similarly, a sealed class allows you to define a limited set of subclasses that are relevant to a specific context.

Comparison with Other Concepts

  • Abstract Classes: Like sealed classes, abstract classes cannot be instantiated, but they allow subclasses to be defined anywhere.
  • Enums: Enums define a fixed set of constants, but they cannot hold complex data or behaviors like sealed classes can.
  • Syntax of Sealed Classes

To declare a sealed class, use the sealed keyword followed by the class name. Here's the basic syntax:

Example

sealed class Shape

Declaring Subclasses

Subclasses of a sealed class are defined within the same file:

Example

sealed class Shape {
    class Circle(val radius: Float) : Shape()
    class Square(val length: Int) : Shape()
    class Rectangle(val length: Int, val breadth: Int) : Shape()
    object NotAShape : Shape()
}

In this example, Shape is a sealed class with three subclasses: Circle, Square, and Rectangle. The NotAShape object represents a special case.

Multiple Working Examples

Example 1: Basic Usage of Sealed Classes

Here’s a simple example demonstrating how to use sealed classes to represent different shapes:

Example

sealed class Shape {
    class Circle(val radius: Float) : Shape()
    class Square(val length: Int) : Shape()
}

fun main() {
    val myShape: Shape = Shape.Circle(5.0f)

    when (myShape) {
        is Shape.Circle -> println("This is a Circle with radius: ${myShape.radius}")
        is Shape.Square -> println("This is a Square with length: ${myShape.length}")
    }
}

Output:

Output

This is a Circle with radius: 5.0

Example 2: Using Sealed Classes with When Expressions

This example expands on the previous one by calculating areas based on the shape type:

Example

sealed class Shape {
    class Circle(val radius: Float) : Shape()
    class Square(val length: Int) : Shape()
    class Rectangle(val length: Int, val breadth: Int) : Shape()
}

fun calculateArea(shape: Shape): Float {
    return when (shape) {
        is Shape.Circle -> 3.14f * shape.radius * shape.radius
        is Shape.Square -> (shape.length * shape.length).toFloat()
        is Shape.Rectangle -> shape.length * shape.breadth.toFloat()
    }
}

fun main() {
    val circle = Shape.Circle(5.0f)
    val square = Shape.Square(4)
    val rectangle = Shape.Rectangle(3, 6)

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

Output:

Output

Circle Area: 78.5
Square Area: 16.0
Rectangle Area: 18.0

Example 3: Sealed Class with Additional Properties

You can also add more complexity to your sealed classes by including additional properties and methods:

Example

sealed class Shape {
    class Circle(val radius: Float) : Shape() {
        fun circumference() = 2 * 3.14f * radius
    }
    class Square(val length: Int) : Shape()
}

fun main() {
    val circle = Shape.Circle(5.0f)
    println("Circle Circumference: ${circle.circumference()}")
}

Output:

Output

Circle Circumference: 31.400000000000002

Example 4: Adding a "Not a Shape" Case

In some situations, you might want to include a case that represents an invalid state or an absence of a shape:

Example

sealed class Shape {
    class Circle(val radius: Float) : Shape()
    class Square(val length: Int) : Shape()
    object NotAShape : Shape()
}

fun describeShape(shape: Shape): String {
    return when (shape) {
        is Shape.Circle -> "This is a Circle with radius: ${shape.radius}"
        is Shape.Square -> "This is a Square with length: ${shape.length}"
        Shape.NotAShape -> "This is not a valid shape."
    }
}

fun main() {
    val invalidShape = Shape.NotAShape
    println(describeShape(invalidShape))
}

Output:

Output

This is not a valid shape.

Common Mistakes

  1. Defining Subclasses Outside the Sealed Class File:
  • Mistake: Attempting to define subclasses in another file.
  • Why It's Wrong: Sealed classes require all subclasses to be defined in the same file to maintain type safety.
  • Correct Approach: Ensure all subclasses are in the same file as the sealed class.
  1. Instantiating a Sealed Class Directly:
  • Mistake: Trying to create an instance of a sealed class directly.
  • Why It's Wrong: Sealed classes are abstract and cannot be instantiated directly.
  • Correct Approach: Instantiate the subclasses instead.
  • Best Practices

  • Use Sealed Classes for State Management: They are excellent for representing states in applications, such as loading, success, or error states.
  • Avoid Complex Hierarchies: Keep the subclass hierarchy simple to prevent confusion.
  • Leverage When Expressions: Always use when expressions with sealed classes to ensure all cases are handled.
  • Practice Exercises

  1. Create a sealed class for different types of Animals with subclasses like Dog, Cat, and Bird. Implement a method to print the sound each animal makes.
  2. Implement a simple calculator using a sealed class for different operations like Addition, Subtraction, Multiplication, and Division. Handle operations using a when expression.
  3. Model a traffic light system using a sealed class with states like Red, Yellow, and Green. Create a method that prints actions based on the light's state.

With these exercises, you can deepen your understanding of sealed classes and their practical applications in Kotlin. Happy coding!

Input Required

This code uses input(). Please provide values below:

🤖 Coding Mentor
🤖

Hi! I'm your coding mentor

Ask me anything about programming:

• Python, Java, C++, JavaScript

• Algorithms & Data Structures

• Debugging & Code Help