Sealed Classes

Sealed Classes #

Description #

Enhance the Java programming language with sealed classes and interfaces. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.

Sealed classes/interfaces where introduced through JEP-409 in Java 17. This functionality was avaiable as preview feature in Java 15 and 16 through JEP-360 and JEP-397.

Goals #

Main goals of this language improvement:

  1. Allow the author of a class or interface to control which code is responsible for implementing it.
  2. Provide a more declarative way than access modifiers to restrict the use of a superclass.
  3. Support future directions in pattern matching by providing a foundation for the exhaustive analysis of patterns.

Syntax #

This language feature relies on 3 new keywords:

  • sealed - Used to declare that a class/interface has a fixed set of allowed sub-classes/interfaces.
  • non-sealed - A sub-class/interface of a sealed super-class/interface can use the non-sealed keyword to allow un-restricted sub-classes/implementations.
  • permits - When working with a sealed class/interface permits keyword is used to declare thed fixed set of sub-classes/interfaces that exist.

Sub-classes/interfaces of a sealed type are required to declare if and how future subtypes can be defined. This is achieved though the use of final (only for classes), sealed and non-sealed keywords.

Package structure

When working with a sealed class there are two options when it comes to package structure:

  1. Make sure all sub-types are in the same package
  2. Use the module system introduced in Java 9 which will allow the use of sub-types that are not in the same package

Usage #

In order to highlight the benefits of sealed types we’ll use as example a game engine with the following requirements :

  • two types of Characters : Hero and Monster
  • allow the engine user to define Hero characters
  • restrict the definition of Monster characters

Considering the above requirements the class structure can look like this :

package com.javafeatures.sealedgame;

sealed interface Character permits Hero, Monster {}

non-sealed abstract class Hero implements Character { }

sealed class Monster implements Character permits Cyclop, Dragon { }

final class Dragon extends Monster { }

final class Cyclop extends Monster { }

Switch pattern matching (Preview) #

While currently still in preview, the combination of switch expressions and sealed classes allows us to create exhaustive checks on the class type.

package com.javafeatures.sealedgame;

private static Color getAvatarBackgroundColor(Character character) {
    return switch (character) {
        case Hero hero -> Color.GREEN;
        case Monster monster -> Color.RED;
    };
}

The compiler will make sure all possibilities are accounted for by either specifying all types in the switch or by providing a default branch. In the above example if a new Character type would be added the developer would be forced by the compiler to handle it in the switch expresion.