Sealed classes and interfaces in Java

Let’s say you are creating a base class and you want it be extended by other developers to reuse the code.

Say you developed a base class named Animal and you want to allow another developer to create a Cat class which can extend this base class.

So you made the base class as public and anyone could extend it in their class.

And there came a developer who created a Sparrow class and extended the base class Animal just because he wanted to reuse one of the methods in the Animal class (say a method named walk() – after all both birds and animals walk he reasoned)

But you don’t want to allow it! . You want only certain sub classes to extend your base class.

You can’t mark your class as final because that will restrict any class to extend it.

You can’t mark it as package-private (a class with no modifier) because all the classes in a package will be able to subclass it then.

Java didn’t allow such a feature for a long time.

It has come up with such a feature in Java 15.

The feature is called ‘Sealed Classes’.

As the name suggests you can seal a class .

You can say which subclasses are permitted to extend a base class. By saying you define a cleaner restricted design.

Going back to the example defined above , if Animal class permits only Cat and Dog to extend it , a compiler error will be thrown when a Sparrow class tries to extend it.

Below is an example of a sealed class:

package sealed;

public sealed class Animal permits Cat,Dog{



}

The class is prefixed by the keyword ‘sealed’ and the class name is followed by the keyword ‘permits’ which is followed by the permitted subclasses seperated by comma.

There is one more caveat in this. A subclass which extends a sealed class should specify how it propagates the sealing mechanism of its super class.

Will it not allow any other class to extend it ?

Then it should be final ,

Will it allow only certain sub classes to extend it?

Then it should be sealed

Will it allow any number of classes to extend it?

Then it should non-sealed.

You need to specify any of these three modifiers else the compiler will complain.

For example , below is the Cat subclass I created:

package sealed;

public non-sealed class Cat extends Animal{


}

It is non-sealed meaning any number of classes can extend Cat class.

Below is the Dog subclass I created:

package sealed;

public final class Dog extends Animal{


}

It is final meaning no class can extend it.

Also,

  • A sealed class and its permitted sub classes should be in the same package. If you are using modules , then they can be in the same module
  • A permitted subclass should directly extend its super class ( it cannot extend it through another sub class)

All the above said scenarios and rules apply for interfaces as well. And they are called ‘Sealed Interfaces’.

Also Records can be used with Sealed Interfaces. ( Records are a new feature in Java 14 and you can know about it here)

Here is a sample AnimalInterface which permits only CatRecord and DogRecord to extend it:

package sealed;

public sealed interface AnimalInterface permits CatRecord,DogRecord{


}

As you see both the ‘sealed’ and ‘permits’ keywords are used the same way as in Sealed Classes.

Now let’s look at the records which extend the above interface:

package sealed;


public record CatRecord(String name,int breed) implements AnimalInterface{}
package sealed;

public record DogRecord(String name,int breed) implements AnimalInterface{}

Since records are implicitly final there is no need to specify any of the sealed,non-sealed and final modifiers to records.

The combination of using sealed interfaces and records is referred to as algebraic data type.

An algebraic data type is a combination of products and sums.

In this case , the Animal interface is an algebraic data type.

It represents the sum of two different animals (Cats and Dogs)

And in turn each animal can have different products (Eg) The name and breed field values of Cat can be combined to create many Cats)

Sealed classes became a permanent feature in Java 17.

Here is a code sample:


public class SealedClasses{


    public static void main(String a[]){

        Dog dog = new Dog();

        dog.walk();

    }
}


 sealed class Animal permits Cat,Dog,Lion{


    public void walk(){

        System.out.println("I am animal walking");
    }

    
}

 non-sealed class Dog extends Animal{


}

 sealed class Lion extends Animal permits LionCub{


}
 final class Cat extends Animal{



}

 final class LionCub extends Lion{

}




You can see Animal class permits Dog, Cat and Lion .

And Dog class is final (no class can inherit it),

Cat class is non-sealed (any class can inherit it),

Lion class is sealed (only those classes permitted can inherit it)

LionCub class is extended from Lion class and it is final (no class can inherit it).

The classes compile fine and produce the below output:

Now let me try to create a new class Sparrow and try to extend Animal class :

final class Sparrow extends Animal{

    
}

I get the below error on compilation:

Let me try to change the class Dog and not provide any modifier (sealed , non-sealed or final):

class Dog extends Animal{


}

I get the below error:

Quoting Open JDK documentation:

In summary, it should be possible for a superclass to be widely accessible (since it represents an important abstraction for users) but not widely extensible (since its subclasses should be restricted to those known to the author).

That’s it!

4 thoughts

  1. I find the `non-sealed` keyword totally unconventional. It it the only keyword in Java that is hyphenated. I can be changed to nonsealed OR open. Do we even need that keyword?

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s