Java 17 – Sealed classes

Prior to Sealed Classes feature there were two ways a developer could prevent a class to be extended:

  1. by declaring the class as final where no one can extend this class.
public final class MyClass { }

2. by making the class package private where no one outside the package can access the class and hence no one outside the package can extend the class.

package mypackage;
class MyClass { }
package mypackage;
public class MySub extends MyClass{ }

The first approach is very restrictive but the superclass is accessible outside the package. The second approach is less restrictive but the superclass is not accessible outside so we cannot have something like:

MyClass myInstance = getRightSubClassInstance();

So restricting a class from the extension is not well supported.
Now, why do we need to restrict a class from being extended? This is to support pattern matching in switch statements.

Consider enums, they are fixed instances for a given data type. Generally, we use it to model constants but we can even model enums to have their state and behavior thereby giving us a fixed set of instances with their state and behavior. As they are restricted in their possible values so it would be easier to write a switch statement without having to worry about writing a default clause.

On similar lines, the idea of restricting the number of subclasses for a given type will make it easier to write a switch statement with specific subclasses in the case statement without worrying about writing a default clause to handle any future implementations of the given type.

To address the above two issues, the concept of sealed classes was introduced as part of JEP-409 in Java 17.

A sealed class prevents any other class from extending it except for those classes which have been granted permission via the permits clause as shown below:

package sealed;
public sealed class MySuper permits MySub1, MySub2{ }

The above class MySuper is defined in a package sealed permitting only classes MySub1 and MySub2 in the same package to extend it. The subclasses are defined as:

package sealed;
public final class MySub1 extends MySuper{ }

package sealed;
public final class MySub2 extends MySuper{ }

Both the subclasses should declare themselves to be one of:

  • final – which means no other classes can extend them and thus the heirarchy is frozen and well defined
  • sealed – which means the subclasses in turn will permit some classes to be able to extend them
  • non-sealed – which means the subclasses are not offering any restriction and any class can extend them freely.

If the subclasses are in different packages than the superclass then if they are in the same-named module it is permissible to refer them in the permits clause using their fully qualified class names otherwise if the subclasses are in different packages and they all are in an unnamed module (i.e not defined in any module) then you cannot refer to them in the permits clause and you will get the following error

The sealed concept is applicable to the interfaces as well.

Leave a Reply

%d bloggers like this: