Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8227043

JEP 360: Sealed Classes (Preview)


    • Author:
      Brian Goetz
    • JEP Type:
    • Exposure:
    • Subcomponent:
    • Scope:
    • Discussion:
      amber dash dev at openjdk dot java dot net
    • Effort:
    • Duration:
    • JEP Number:



      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.


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


      • It is not a goal to provide new forms of access control such as "friends".
      • It is not a goal to change final in any way.


      In Java, a class hierarchy enables the reuse of code via inheritance: The methods of a superclass can be inherited (and thus reused) by many subclasses. However, the purpose of a class hierarchy is not always to reuse code. Sometimes, its purpose is to model the various possibilities that exist in a domain, such as the kinds of shapes supported by a graphics library or the kinds of loans supported by a financial application. When the class hierarchy is used in this way, restricting the set of subclasses can streamline the modeling.

      For example, in a graphics library, the author of a class Shape may intend that only particular classes can extend Shape, since much of the library's work involves handling each kind of shape in the appropriate way. The author is interested in the clarity of code that handles known subclasses of Shape, and not interested in writing code to defend against unknown subclasses of Shape. Allowing arbitrary classes to extend Shape, and thus inherit its code for reuse, is not a goal in this case. Unfortunately, Java assumes that code reuse is always a goal: If Shape can be extended at all, then it can be extended by any number of classes. It would be helpful to relax this assumption so that an author can declare a class hierarchy that is not open for extension by arbitrary classes. Code reuse would still be possible within such a closed class hierarchy, but not beyond.

      Java developers are familiar with the idea of restricting the set of subclasses because it often crops up in API design. The language provides limited tools in this area: either make a class final, so it has zero subclasses, or make a class or its constructor package-private, so it can only have subclasses in the same package. An example of a package-private superclass appears in the JDK:

      package java.lang;
      abstract class AbstractStringBuilder {...}
      public final class StringBuffer  extends AbstractStringBuilder {...}
      public final class StringBuilder extends AbstractStringBuilder {...}

      The package-private approach is useful when the goal is code reuse, such as the subclasses of AbstractStringBuilder sharing its code for append. However, the approach is useless when the goal is modeling alternatives, since user code cannot access the key abstraction -- the superclass -- in order to switch over it. It is not possible to allow users to access the superclass without also allowing them to extend it. (Even within a graphics library that declares Shape and its subclasses, it would be unfortunate if only one package could access Shape.)

      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). Such a superclass should be able to express that it is co-developed with a given set of subclasses, both to document intent for the reader and to allow enforcement by the Java compiler. At the same time, the superclass should not unduly constrain its subclasses by, e.g., forcing them to be final or preventing them from defining their own state.


      A sealed class or interface can be extended or implemented only by those classes and interfaces permitted to do so.

      A class is sealed by applying the sealed modifier to its declaration. Then, after any extends and implements clauses, the permits clause specifies the classes that are permitted to extend the sealed class. For example, the following declaration of Shape specifies three permitted subclasses:

      package com.example.geometry;
      public sealed class Shape
          permits Circle, Rectangle, Square {...}

      The classes specified by permits must be located near the superclass: either in the same module (if the superclass is in a named module) or in the same package (if the superclass is in the unnamed module). For example, in the following declaration of Shape, its permitted subclasses are all located in different packages of the same named module:

      package com.example.geometry;
      public sealed class Shape 
          permits com.example.polar.Circle,
                  com.example.quad.simple.Square {...}

      When the permitted subclasses are small in size and number, it may be convenient to declare them in the same source file as the sealed class. When they are declared in this way, the sealed class may omit the permits clause, and the Java compiler will infer the permitted subclasses from the declarations in the source file (which may be auxilliary or nested classes). For example, if the following code is found in Shape.java, then the sealed class Shape is inferred to have three permitted subclasses:

      package com.example.geometry;
      sealed class Shape {...}
      ... class Circle    extends Shape {...}
      ... class Rectangle extends Shape {...}
      ... class Square    extends Shape {...}    

      Sealing a class restricts its subclasses. User code can inspect an instance of a sealed class with an if-else chain of instanceof tests, one test per subclass; no catch-all else clause is needed. For example, the following code looks for the three permitted subclasses of Shape:

      Shape rotate(Shape shape, double angle) {
          if (shape instanceof Circle) return shape;
          else if (shape instanceof Rectangle) return shape.rotate(angle);
          else if (shape instanceof Square) return shape.rotate(angle);

      Unfortunately, the Java compiler cannot ensure that the instanceof tests cover all the permitted subclasses of Shape. For example, no compile-time error would be given if the instanceof Rectangle test was omitted.

      A significant benefit of sealing will be realized in a future release in conjunction with pattern matching. Instead of inspecting an instance of a sealed class with if-else, user code will be able to switch over the instance using type test patterns (JEP 375). This allows the Java compiler to check that the patterns are exhaustive. For example, given the following code, the compiler will infer that every permitted subclass of Shape is covered, so no default clause (or other total pattern) is needed; moreover, the compiler will give an error if any of the three cases are missing:

      Shape rotate(Shape shape, double angle) {
          return switch (shape) {
              case Circle c    -> c;  // no action needed
              case Rectangle r -> r.rotate(angle);
              case Square s    -> s.rotate(angle);

      A sealed class imposes three constraints on its permitted subclasses (the classes specified by its permits clause):

      1. The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, the same package.

      2. Every permitted subclass must directly extend the sealed class.

      3. Every permitted subclass must choose a modifier to describe how it continues the sealing initiated by its superclass:

        • A permitted subclass may be declared final to prevent its part of the class hierarchy from being extended further.
        • A permitted subclass may be declared sealed to allow its part of the hierarchy to be extended further than envisaged by its sealed superclass, but in a restricted fashion.
        • A permitted subclass may be declared non-sealed so that its part of the hierarchy reverts to being open for extension by unknown subclasses. (A sealed class cannot prevent its permitted subclasses from doing this.)

      As an example of the third constraint, Circle may be final while Rectangle is sealed and Square is non-sealed:

      package com.example.geometry;
      public sealed class Shape
          permits Circle, Rectangle, Square {...}
      public final class Circle extends Shape {...}
      public sealed class Rectangle extends Shape 
          permits TransparentRectangle, FilledRectangle {...}
      public final class TransparentRectangle extends Rectangle {...}
      public final class FilledRectangle extends Rectangle {...}
      public non-sealed class Square extends Shape {...}    

      One and only one of the modifiers final, sealed, and non-sealed must be used by each permitted subclass. It is not possible for a class to be both sealed (implying subclasses) and final (implying no subclasses), or both non-sealed (implying subclasses) and final (implying no subclasses), or both sealed (implying restricted subclasses) and non-sealed (implying unrestricted subclasses).

      (The final modifier can be considered as a strong form of sealing, where extension/implementation is prohibited completely. That is, final is conceptually equal to sealed + a permits clause which specifies nothing; note that such a permits clause cannot be written in Java.)

      Absract classes. A class which is sealed or non-sealed may be abstract, and have abstract members. A sealed class may permit subclasses which are abstract (providing they are then sealed or non-sealed, rather than final).

      Class accessibility. Because extends and permits clauses make use of class names, a permitted subclass and its sealed superclass must be accessible to each other. However, permitted subclasses need not have the same accessibility as each other, or as the sealed class. In particular, a subclass may be less accessible than the sealed class; this means that, in a future release when pattern matching is supported by switches, some users will not be able to exhaustively switch over the subclasses unless a default clause (or other total pattern) is used. Java compilers will be encouraged to detect when a user's switch is not as exhaustive as the user imagined it would be, and customize the error message to recommend a default clause.

      Sealed interfaces

      Similar to the story for classes, an interface is sealed by applying the sealed modifier to the interface. After any extends clause to specify superinterfaces, the implementing classes and subinterfaces are specified with a permits clause. For example:

      package com.example.expression;
      public sealed interface Expr
          permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}
      public final class ConstantExpr implements Expr {...}
      public final class PlusExpr     implements Expr {...}
      public final class TimesExpr    implements Expr {...}
      public final class NegExpr      implements Expr {...}

      Sealed classes and Records

      Sealed classes work well with records (JEP 384), another preview feature of Java 15. Records are implicitly final, so a sealed hierarchy with records is slightly more concise than the example above:

      package com.example.expression;
      public sealed interface Expr
          permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}
      public record ConstantExpr(int i)       implements Expr {...}
      public record PlusExpr(Expr a, Expr b)  implements Expr {...}
      public record TimesExpr(Expr a, Expr b) implements Expr {...}
      public record NegExpr(Expr e)           implements Expr {...}

      The combination of sealed classes and records is sometimes referred to as algebraic data types: Records allow us to express product types, and sealed classes allow us to express sum types.

      Sealed classes in the JDK

      An example of how sealed classes might be used in the JDK is in the java.lang.constant package that models descriptors for JVM entities:

      package java.lang.constant;
      public sealed interface ConstantDesc
          permits String, Integer, Float, Long, Double,
                  ClassDesc, MethodTypeDesc, DynamicConstantDesc {...}
      // ClassDesc is designed for subclassing by JDK classes only
      public sealed interface ClassDesc extends ConstantDesc
          permits PrimitiveClassDescImpl, ReferenceClassDescImpl {...}
      final class PrimitiveClassDescImpl implements ClassDesc {...}
      final class ReferenceClassDescImpl implements ClassDesc {...} 
      // MethodTypeDesc is designed for subclassing by JDK classes only
      public sealed interface MethodTypeDesc extends ConstantDesc
          permits MethodTypeDescImpl {...}
      final class MethodTypeDescImpl implements MethodTypeDesc {...}
      // DynamicConstantDesc is designed for subclassing by user code
      public non-sealed abstract class DynamicConstantDesc implements ConstantDesc {...}

      Java Grammar

        {ClassModifier} class TypeIdentifier [TypeParameters]
        [Superclass] [Superinterfaces] [PermittedSubclasses] ClassBody
        (one of)
        Annotation public protected private
        abstract static sealed final non-sealed strictfp
        permits ClassTypeList
        ClassType {, ClassType}

      JVM support for sealed classes

      The Java Virtual Machine recognizes sealed classes and interfaces at runtime, and prevents extension by unauthorized subclasses and subinterfaces.

      Although sealed is a class modifier, there is no ACC_SEALED flag in the ClassFile structure. Instead, the class file of a sealed class has a PermittedSubclasses attribute which implicitly indicates the sealed modifier and explicitly specifies the permitted subclasses:

      PermittedSubclasses_attribute {
          u2 attribute_name_index;
          u4 attribute_length;
          u2 number_of_classes;
          u2 classes[number_of_classes];

      The list of permitted subclasses is mandatory—even when the permitted subclasses are inferred by the compiler, those inferred subclasses are explicitly included in the PermittedSubclasses attribute.

      The class file of a permitted subclass carries no new attributes.

      When the JVM attempts to defines a class whose superclass or superinterface has a PermittedSubclasses attribute, the class being defined must be named by the attribute. Otherwise, an IncompatibleClassChangeError is thrown.

      Reflection API

      The following public methods will be added to java.lang.Class:

      • java.lang.constant.ClassDesc[] getPermittedSubclasses()
      • boolean isSealed()

      The method getPermittedSubclasses() returns an array containing java.lang.constant.ClassDesc objects representing all the permitted subclasses of the class if it is sealed, and returns an empty array if the class is not sealed.

      The method isSealed returns true if the given class or interface is sealed. (Compare with isEnum.)


      Some languages have direct support for algebraic data types (ADTs), such as Haskell's data feature. It would be possible to express ADTs more directly and in a manner familiar to Java developers through a variant of the enum feature, where a sum of products could be defined in a single declaration. However, this would not support all the desired use cases, such as those where sums range over classes in more than one compilation unit, or sums that range over classes that are not products.

      The permits clause allows a sealed class, such as the Shape class shown earlier, to be accessible-for-invocation by code in any module, but accessible-for-implementation by code in only the same module as the sealed class (or same package if in the unnamed module). This makes the type system more expressive than the access control system. With access control alone, if Shape is accessible-for-invocation by code in any module (because its package is exported), then Shape is also accessible-for-implementation in any module; and if Shape is not accessible-for-implementation in any other module, then Shape is also not accessible-for-invocation in any other module.


      Sealed classes do not depend on records (JEP 384) or pattern matching (JEP 375), but they work well with both.


          Issue Links



              • Assignee:
                vromero Vicente Arturo Romero Zaldivar
                briangoetz Brian Goetz
                Vicente Arturo Romero Zaldivar
                Reviewed By:
                Alex Buckley
                Endorsed By:
                Mark Reinhold
              • Votes:
                0 Vote for this issue
                14 Start watching this issue


                • Created: