Java Sealed Classes and Interfaces: Controlling Inheritance for Better Code Design
Explore Java's Sealed Classes and Interfaces, introduced in Java 15 and made standard in Java 17. Learn how these features give developers fine-grained control over inheritance, allowing them to specify which subtypes can extend a class or interface for better security and code design.
Java - Sealed Classes and Interfaces
Java 15 introduced a sealed class as a preview feature, providing fine-grained control over inheritance. Java 16 enhanced this feature slightly and maintained it as a preview. By Java 17, sealed classes and interfaces became standard features. A sealed class or interface allows developers to control which subtypes can extend it, preventing unwanted inheritance.
Key Points of Sealed Classes
- A sealed class is declared using the
sealed
keyword. - Sealed classes allow the declaration of permitted subtypes using the
permits
keyword. - A class extending a sealed class must be declared as either
sealed
,non-sealed
, orfinal
. - Sealed classes help create a finite and determinable hierarchy of classes in inheritance.
Sealed Interface
An interface can be marked as a sealed interface using the sealed
keyword, and then the permits
keyword can specify which interfaces can extend it.
Syntax
public sealed interface Person permits Employee, Manager {
}
Sealed Interface Example
In this example, we've created a sealed interface Person
that permits the Employee
and Manager
interfaces to extend it. Each interface has different methods to retrieve the ID of a person. We use the instanceof
operator to check the instance type and retrieve the corresponding ID.
Syntax
package com.tutorialsarena;
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;
}
}
Output
Id: 23
Sealed Class
Similar to sealed interfaces, a class can also be marked as a sealed class using the sealed
keyword, followed by the permits
keyword to specify which subclasses can extend this class.
Syntax
public abstract sealed class Person permits Employee, Manager {
}
Constraints of Sealed Classes
- The permitted subclass must be part of the same module as the sealed class.
- The permitted subclass must extend the sealed class.
- The permitted subclass must use one of the following modifiers:
final
,sealed
, ornon-sealed
.
Sealed Class Example
In this example, we've created a sealed abstract class Person
that permits the Employee
and Manager
classes to extend it. Each class implements different methods to retrieve the ID of a person, utilizing the instanceof
operator to determine the instance type.
Syntax
package com.tutorialsarena;
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;
}
}
Output
Id: 23