Java 15 - Sealed Classes Preview Feature (JEP 360)

Java is missing various “hot” features from more modern JVM languages like Scala or Kotlin. One feature it's missed until the release of JDK15 (with JEP 360) is sealed classes. Although the title of JEP is about sealed class, but it's applicable to interface too.

Sealed Class and Interface

Sealed classes and interfaces used to restrict another class from extend or implement the class.

A class or interface is sealed by using sealed keyword in its declaration, then at the end using permits keyword and specifies all classes that are permitted to inherits this sealed class. As example, we have two sealed 'classes', one class called Printer and another one is an interface called Printable. All examples under package com.dariawan.jdk15.sealed:

public sealed class Printer permits LaserPrinter, InkjetPrinter { } public sealed interface Printable permits LaserPrinter, ThermalPrinter { }

To make the sample more interesting, I also added one parent class which is not a sealed class:

public class NotSealedParent { }

To extends or inherits those two sealed classes, we create LaserPrinter, InkjetPrinter, and ThermalPrinter. Inherited classes expecting non-sealed, final, or another sealed modifiers:

  • non-sealed: allowed this class as a parent class, can be extended by any subclasses
  • final: prevent this class as a parent class, cannot be extended further. To refresh the understanding about final keyword, please read Inheritance in Java.
  • sealed: allowed this class as a parent class that only can be extended by its permitted subclasses.
public final class LaserPrinter extends Printer implements Printable { } public non-sealed class InkjetPrinter extends Printer { } public class InkjetPhotoPrinter extends InkjetPrinter { } public final class ThermalPrinter extends NotSealedParent implements Printable { }

As example for inherits non-sealed parent class, class InkjetPhotoPrinter is extended from class InkjetPrinter.

Class that is not permitted by as sealed class will get a compilation error when tries to extend it. Below ThermalPrinter compilation will resulted in following error: class is not allowed to extend sealed class: com.dariawan.jdk15.Printer, if we extends from Printer:

public final class ThermalPrinter extends Printer implements Printable { }

Changes in java.lang.Class<T>

Java’s Reflection API also extended to add support for sealed classes. There are two new methodsassociated with sealed classes:

  • boolean isSealed(): returns true if this 'class' is sealed class or interface.
  • ClassDesc[] permittedSubclasses(): if it is a sealed class, returns a ClassDesc array containing subclasses permitted to extend or implement this class or interface.

Let's check below example to use these new API:

SealedClassExample.java
package com.dariawan.jdk15.sealed;

import java.lang.constant.ClassDesc;

public class SealedClassExample {
    
    public static void main(String[] args) {
        System.out.println("Printer is sealed: " + Printer.class.isSealed());
        System.out.println("Printer permittedSubclasses:");
        for (ClassDesc cd : Printer.class.permittedSubclasses()) {
            System.out.println(cd.toString());
        }
        
        System.out.println("Printable is sealed: " + Printable.class.isSealed());
        System.out.println("Printable permittedSubclasses:");
        for (ClassDesc cd : Printable.class.permittedSubclasses()) {
            System.out.println(cd.toString());
        }
        
        System.out.println("NotSealedParent is sealed: " + NotSealedParent.class.isSealed());
        System.out.println("NotSealedParent permittedSubclasses:");
        for (ClassDesc cd : NotSealedParent.class.permittedSubclasses()) {
            System.out.println(cd.toString());
        }
        
        System.out.println("LaserPrinter is sealed : " + LaserPrinter.class.isSealed());
        System.out.println("LaserPrinter superclass: " + LaserPrinter.class.getSuperclass());
        
        System.out.println("ThermalPrinter is sealed : " + ThermalPrinter.class.isSealed());
        System.out.println("ThermalPrinter superclass: " + ThermalPrinter.class.getSuperclass());
    }
}
                    

Run the program, here the result:

Printer is sealed: true
Printer permittedSubclasses:
ClassDesc[LaserPrinter]
ClassDesc[InkjetPrinter]
Printable is sealed: true
Printable permittedSubclasses:
ClassDesc[LaserPrinter]
ClassDesc[ThermalPrinter]
NotSealedParent is sealed: false
NotSealedParent permittedSubclasses:
LaserPrinter is sealed : false
LaserPrinter superclass: class com.dariawan.jdk15.sealed.Printer
ThermalPrinter is sealed : false
ThermalPrinter superclass: class com.dariawan.jdk15.sealed.NotSealedParent