Introduction
Sealed Classes are an important new feature introduced as a preview feature of JDK 15 and 16 but as a permanent candidate in JDK 17 (LTS). It helps you to have a finer controller over class hierarchy. Inheritance is the main pillar for OOPS but if we see the current framework, it can easily be misused. Any class can extend another unless it's not final. What if you as a developer want to allow few classes to extend it but not all. Sealed classes come to rescue you.
Let us look at sealed classes and their usage.
Do note that if you want to use sealed classes you must have JDK 15 + on the classpath.
Implementation
We will create a class Character
and permit only Hero
and Villain
to extend it. We will also create child clesses of Hero
and Villain
Below is the structure we will follow
package sealed;
public sealed class Character permits Hero, Villan {
public boolean hasPositiveVibes() {
return true;
}
}
We have added a method hasPositiveVibes
just to demonstrate the usage. We will Override
it in Villan class to send negative vibes. :)
Let us create Hero and Villain in the same package. Do note that any class permitted by a sealed class must be in the same package or else it will give the compilation error.
Hero.java
package sealed;
import sealed.Character;
import sealed.JusticeHammer;
import sealed.WhiteClaw;
public sealed class Hero extends Character permits WhiteClaw, JusticeHammer {
}
Here Hero is again a sealed class and imposing us to tell all those allowed classes. Any class extending from a sealed class must be defined sealed
, non-sealed
or final
.
sealed
: Allows limited set classes to extend itselfnon-sealed
: Allows any other class to extendfinal
: Allows none
We can now have Whileclaw
and JusticeHammer
as a final class.
package sealed;
public final class WhiteClaw extends Hero {
}
package sealed;
public final class JusticeHammer extends Hero {
}
Now let us see an example of a non-sealed class. As we know that there are very few real heroes in the world fighting for a cause to so many bad guys. We can't allow everyone to be believed as Hero so we kept a tight control at Hero class and giving permission to only a few. There are many Villains who can break the law, so we have kept Villan as non-sealed.
Do note that we need not define the permit clause with `non-sealed
classes. The classes extending to non-sealed can be in different packages as well
We also have overridden the hasPositiveVibes
method of Character
to send negative vibes from Villain(s)
Villain.java
package sealed;
public non-sealed class Villain extends Character {
@Override
public boolean hasPositiveVibes(){
return false;
}
}
public class Outlaw extends Villain {
}
public class MrKiller extends Villain {
}
non-sealed classes can be extended from packages outside of the parent
Villains don't follow any order, they want to expand beyond earth. Mars is their first target. MrKiller has a son who now lives on Mars, he is MartianKiller. Let us create a package outside the sealed World.
package mars;
import sealed.MrKiller;
public class MartianKiller extends MrKiller {
}
- This also shows non-sealed classes can be extended from packages outside of the parent.
The permit clause is optional if subclasses are in the same file.
Wait, there is something wrong, World is not only about superheroes and Villains but also about (Humans) Man, Women, and a few who don't want to reveal their identity in this chaotic order.
Let us add Human as Character. Now our Character looks like
public sealed class Character permits Hero, Villain,Humans {
public boolean hasPositiveVibes() {
return true;
}
}
Also, let us look at Humans
package sealed;
/*
permits' clause has been removed as its permitted
classes have been defined in the same file.
*/
public sealed class Humans extends Character {
}
final class Man extends Humans {
}
final class Woman extends Humans {
}
- As you can see, the permit clause lass in Human has been omitted. The permit clause is optional if subclasses are in the same file.
Now MartianKiller and Outlaw are Villains. At this point, we can test our code. Below Main class will create instances of Hero and Villian and test if they send positive or negative vibes.
package sealed;
public class Main {
public static void main(String[] args) {
Character killer= new MartianKiller();
System.out.println(killer.hasPositiveVibes());
Character claw= new WhiteClaw();
System.out.println(claw.hasPositiveVibes());
}
}
The output will be false
and true
.
TL;DR
- All sealed classes must tell whom they permit
- All classes extending to sealed classes has to be final/sealed/non-sealed
- Any class extending non-sealed doesn't have to be explicitly sealed, non-sealed, or final
- All sealed classes and their child must be in the same package
- non-sealed classes can be extended from anywhere and have no location constraints
- The permit clause is optional if subclasses are in the same file.
- Do note that records can't be a sealed class but can extend one.
I hope this article helped you to understand sealed classes. Sealed interfaces are following the same principles as the sealed classes. If you liked the article, do let me know using a comment or you can share it with your friends.