Unlocking the Power of Sealed Classes

Introduction

Imagine you have a special kind of fruit called “Magical Fruit,” and you want to control who can create new varieties of this fruit. You don’t want just anyone to create new types of Magical Fruit because it could lead to confusion or misuse. So, you decide to seal the Magical Fruit class.

Sealing the Magical Fruit class means that only certain authorized fruit breeders can create new varieties of Magical Fruit. These authorized breeders are like subclasses of the Magical Fruit class. They know the special properties and behaviors that a Magical Fruit should have.

Now, whenever someone wants to create a new variety of Magical Fruit, they need to get permission from the authorized breeders. The breeders will review the proposal and decide if it aligns with the qualities of a Magical Fruit. If approved, the new variety is allowed to be created, and it becomes a subclass of the sealed Magical Fruit class.

Other people who are not authorized breeders cannot create new types of Magical Fruit directly. They can only use or interact with the existing varieties of Magical Fruit that have been created by the authorized breeders.

By sealing the Magical Fruit class, you ensure that only trusted experts can extend or create new types of Magical Fruit. This helps maintain the integrity and consistency of the Magical Fruit ecosystem, preventing unauthorized or confusing varieties from being introduced.

In a similar way, sealed Java classes allow developers to control who can extend or implement certain classes. By sealing a class, you define a limited set of subclasses or implementors that are allowed. This helps maintain the structure and behavior of the class hierarchy, making sure that only authorized classes can extend or implement a sealed class.

How to Seal Java Class

A sealed class is a new feature introduced in Java 15 (JEP 360) that allows the developer to control the extensibility of a class hierarchy. By declaring a class as "sealed," you restrict which other classes can subclass or implement it.

To declare a sealed class, you use the sealed keyword before the class declaration. The code can be found on here github


public sealed class Animal permits Cat, Dog {

    public String walk(){
        return "Animal is walking";
    }
}

By default, all classes within the same package as the sealed class can subclass or implement it. However, you can further restrict access by explicitly listing the permitted subclasses or implementors.

For instance, in the example above, the Cat and Dog classes are allowed to extend or implement the Animal class. Any other class outside the Cat and Dog hierarchy would not be allowed to subclass or implement Animal.

Requirements for Sealed Subclasses

  1. All permitted subclasses must belong to the same module as the sealed class.

  2. Every permitted subclass must explicitly extend the sealed class.

  3. Every permitted subclass must define a modifier: final, sealed, or non-sealed.

Benefits of Sealed Classes

  1. There is no need to explicitely cast after Instance-of call on subclass.
 //Traditional Way with cast- which is mandatory if we don't have sealed class
        if(cat instanceof Animal) {
           String response =  ((Animal)cat).walk();
            System.out.println(response);
        }
 //Without Casting
        if(dog instanceof Animal){
            String response = dog.walk();
            System.out.println(response);
        }

2. Reflection API is supported with Sealed classes or interfaces. There are two methods added in java.lang class to support it.

isSealed → This method can be used to determine if a given class or interface is sealed. It returns true if the class or interface is sealed, indicating that only permitted subclasses or implementors are allowed.

System.out.println("Check if Dog class is sealed " + dog.getClass().isSealed());

getPermittedSubclasses → This method retrieves an array of objects representing all the permitted subclasses of a sealed class. It allows you to obtain information about the classes that are explicitly allowed to extend or implement the sealed class.

 for(Class className: dog.getClass().getSuperclass().getPermittedSubclasses()) {
            System.out.println("Permitted subclass: " + className.getCanonicalName());
        }

Usecases

  1. API Design: Sealed classes can be used to design stable and maintainable APIs. By explicitly specifying which classes can extend or implement a sealed class, API designers can control the surface area and evolution of their APIs. This helps prevent unauthorized extension or implementation and provides better control over the behavior of the API.

  2. Frameworks and Libraries: Sealed classes can be particularly useful in frameworks and libraries where the behavior of certain classes needs to be tightly controlled. By sealing classes, framework developers can ensure that only specific classes can interact with or extend the framework’s core components, enforcing a well-defined and secure architecture.

  3. Enumerations and State Machines: Sealed classes can be used to represent finite states or enumerated values. By sealing the class and defining the permitted subclasses, you can ensure that only the defined set of states or values are used, providing compile-time safety and preventing unexpected states or values.

  4. Data Transfer Objects (DTOs): Sealed classes can be employed to define a hierarchy of data transfer objects where specific subclasses represent different types of data with shared behavior. Sealing the DTO classes ensures that only the intended subclasses can be used, offering better control and maintainability.

Summary

This article introduces the concept of sealed Java classes, which are a way to control the extensibility of class hierarchies. Sealed classes allow developers to specify which subclasses or implementors are allowed, restricting unauthorized extensions or implementations. The article explains that sealed classes offer benefits such as improved code maintenance, stronger encapsulation, and enhanced security. It also mentions that the reflection API supports sealed classes with methods like isSealed and getPermittedSubclasses, which can be used to check if a class is sealed and retrieve the permitted subclasses.

Did you find this article valuable?

Support Amit Himani by becoming a sponsor. Any amount is appreciated!