Java Tutorial

Java Control Statements

Object Oriented Programming

Java Built-in Classes

Java File Handling

Java Error & Exceptions

Java Multithreading

Java Synchronization

Java Networking

Java Collections

Java List Interface

Java Queue Interface

Java Map Interface

Java Set Interface

Java Data Structures

Java Collections Algorithms

Java Miscellaneous

Advanced Java

Java APIs & Frameworks

Java Useful Resources

Java - Sealed Classes and Interfaces



Java 15 introduced a sealed class as a preview feature which provides a fine-grained control over inheritance. Java 16 provides some minor enhancements and keeps this feature as a Preview. With Java 17, sealed class and interface a standard features. A sealed class/interface feature is added to Java to provide developers a fine fine-grained control over inheritance. A sealed class can define the subtypes that are permitted to extend it while other classes cannot extend it.

The following are salient points to consider for a sealed class −

  • A sealed class is declared using a sealed keyword.
  • Sealed classes allow to declaration of which class can be a subtype using the permits keyword.
  • A class extending sealed class must be declared as either sealed, non-sealed, or final.
  • Sealed classes help in creating a finite and determinable hierarchy of classes in inheritance.

Sealed Interface

An interface can be marked as sealed interface using sealed keyword and then using permits keywords, we can add the interfaces which can extend this interface.

public sealed interface Person permits Employee, Manager {
}

Sealed Interface Example

In this example, we've created a sealed interface Person which permits Employee and Manager interfaces to extend it. Employee and Manager interfaces have different methods to get the ID of a person. Now in order to get ID of a person, we're using instanceof operator to check an instance being of Employee or Manager and get the corresponding ID. Thus we can see, that knowing the permissible interfaces upfront helps in development for such scenarios.

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      // create an instance of Manager
      Person manager = new CorpManager(23, "Robert");

      // get the id
      System.out.println("Id: " + getId(manager));
   }
   public static int getId(Person person) {
      // check if person is employee then return employee id
      if (person instanceof Employee) {
         return ((Employee) person).getEmployeeId();
      } 
      // if person is manager then return manager id
      else if (person instanceof Manager) {
         return ((Manager) person).getManagerId();
      }
      return -1;
   }
}

// a sealed interface Person which is to be inherited by Employee
// and Manager interfaces
sealed interface Person permits Employee, Manager {
   String getName();
}

// Employee and Manager interfaces have to extend Person and can be sealed or non-sealed
non-sealed interface Employee extends Person {
   int getEmployeeId();
}

non-sealed interface Manager extends Person {
   int getManagerId();
}

class CorpEmployee implements Employee {
   String name;
   int id;

   public CorpEmployee(int id,String name) {
      this.name = name;
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public int getEmployeeId() {
      return id;
   }		
}

class CorpManager implements Manager {
   String name;
   int id;
   public CorpManager(int id,String name) {
      this.name = name;
      this.id = id;
   }
   public String getName() {
      return name;
   }

   public int getManagerId() {
      return id;
   }		
}

Let us compile and run the above program, this will produce the following result −

Id: 23

Sealed Class

Similar to sealed interface, a class can be marked as sealed class as well using sealed keyword and then using permits keywords, we can add the subclasses which can extend this class.

public abstract sealed class Person permits Employee, Manager {
}

A subclass needs to have a modifier as sealed/final or non-sealed.

public final class Manager extends Person {
}

A subclass with non-sealed is open for all classes to extend.

public non-sealed class Employee extends Person {
}

Constraints

There are few constraint on usage of sealed classes which we should notice while extending a sealed class.

  • Permitted subclass should be part of same module as of sealed class.

  • Permitted subclass has to extend the sealed class.

  • A permitted subclass has to use any one of final, sealed, or non-sealed modifier.

Sealed Class Example

In this example, we've created a sealed abstract class Person which permits Employee and Manager classes to extend it. Employee and Manager classes have different methods to get the ID of a person. Now in order to get ID of a person, we're using instanceof operator to check an instance being of Employee or Manager and get the corresponding ID. Thus we can see, that knowing the permissible SubClasses upfront helps in development for such scenarios.

package com.tutorialspoint;

public class Tester {
   public static void main(String[] args) {
      // create an instance of Manager
      Person manager = new Manager(23, "Robert");

      // get the id
      System.out.println("Id: " + getId(manager));
   }
   public static int getId(Person person) {
      // check if person is employee then return employee id
      if (person instanceof Employee) {
         return ((Employee) person).getEmployeeId();
      } 
      // if person is manager then return manager id
      else if (person instanceof Manager) {
         return ((Manager) person).getManagerId();
      }
      return -1;
   }
}

// a sealed class Person which is to be inherited by Employee
// and Manager classes
abstract sealed class Person permits Employee, Manager {
   String name;
   String getName() {
      return name;
   }
}

// Employee class has to extend Person and should have a modifier
final class Employee extends Person {
   String name;
   int id;
   Employee(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getEmployeeId() {
      return id;
   }
}

// We can mark a sub-class as non-sealed, so that it can be extended if required
non-sealed class Manager extends Person {
   int id;
   Manager(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getManagerId() {
      return id;
   }
}

Let us compile and run the above program, this will produce the following result −

Id: 23
Advertisements