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 features of Java 15 got frozen and they are going to be available for general use in September 2020)

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)

Java 15 is yet to release and so I downloaded the early access build from here: JDK 15

Since IDEs don’t support this version yet I manually compiled my classes using javac command.

Here is the command to compile all the sealed classes and its subclasses:

G:\projects\fullstackdeveloperblog\java\sealed classes\sealed>G:\softwares\openjdk-15-ea+27_windows-x64_bin\jdk-15\bin\javac --enable-preview --release 15 Animal.java Cat.java Dog.java -d out

G:\projects\fullstackdeveloperblog\java\sealed classes\sealed is the name of the package where I created all the classes

G:\softwares\openjdk-15-ea+27_windows-x64_bin\jdk-15\bin\javac is the path of the Java 15 compiler.

–enable-preview is the flag used to indicate that Sealed classes are a preview feature in Java 15

–release this is used to specify the java version and in our case it is 15 (–enable-preview flag requires this )

-d destination folder for the compiled classes

I have specified all the classes together as they are dependent on each other.

The same command can be used to compile AnimalInterface and the records which extend it.

Here is how the compiler complains when you violate the rules of sealed classes:

  • A class trying to extend a super class which does not permit it:
G:\projects\fullstackdeveloperblog\java\sealed classes\sealed>G:\softwares\openjdk-15-ea+27_windows-x64_bin\jdk-15\bin\javac --enable-preview --release 15 Cat.java Animal.java Dog.java Sparrow.java -d out
Sparrow.java:3: error: class is not allowed to extend sealed class: Animal
public class Sparrow extends Animal{
^
  • Trying to create a permitted sub class without any modifier
G:\projects\fullstackdeveloperblog\java\sealed classes\sealed>G:\softwares\openjdk-15-ea+27_windows-x64_bin\jdk-15\bin\javac --enable-preview --release 15 Cat.java Animal.java -d out
Cat.java:3: error: sealed, non-sealed or final modifiers expected
public class Cat extends Animal{
^

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).

Java has added one more weapon in its armory.

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