Sealed Class in Java With Example
Sealed classes and interfaces were the big news in Java 17. In this article, you will learn: Let's start with an example... Let the following class hierarchy be the starting point: Here is the Java source code for the example: Usually, every developer can extend this class hierarchy at any place. An extended structure could look like this (I've colored the added classes light yellow): Now, we may want to restrict the extension of our class hierarchy. For example, we might want to specify that developers may only extend the Why would we want to do that, and how can we do it? There may be several reasons why we want to restrict the free extensibility of our class hierarchy: Now that we know the reasons for constraining a class hierarchy, we move on to the next question: How can we do that? We already know the first possibility… By marking classes as "final", we can prevent their extension in general. A second option would be to mark a class as package-private to permit only subclasses within the same package. However, this would have the consequence that the superclass would no longer be visible outside the package, which is undesirable in most cases. Let's try to use "final" in our example. We mark the classes This limits the extensibility of our class hierarchy, as shown in the following class diagram: To improve clarity, I have removed the crossed-out boxes below the final classes in the following graphic: This means we are on the right track, but we are still far from our goal. What now? Obviously, we can't make This is where sealed classes come in... Using "sealed types", we can implement what is called a "sealed class hierarchy". It works as follows: We extend the code of the With this code, we state the following: The following class diagram shows the constraints added by For the sake of clarity, once more without the crossed-out classes: It looks like we have reached our goal. But one step is still missing… With the changes made so far, our code looks like this: When we try to compile this code, we get the following error message: For preventing accidental openings of the sealed class hierarchy, all classes must be marked Our Our final class hierarchy thus looks like this: There are some particularities to keep in mind when using sealed class hierarchies. The In the following example, Local classes (i.e., classes defined within methods) must not extend sealed classes. The following code shows a local class extending an unsealed class. This code is valid: However, if the outer class is sealed, the local class may not inherit from it: For instanceof tests, the compiler checks whether the class hierarchy allows the check ever to return A incompatible types: Number cannot be converted to String Information from sealed class hierarchies is also included in this check. What this means is best explained with an example: Let's assume we have an interface Thus, the following check is valid: How can this check return The invocation of Now we seal interface The compiler now recognizes that an object of type incompatible types: A cannot be converted to B The introduction of new keywords such as Since Java places a high value on backward compatibility, it was decided not to affect existing code as much as possible. That is made possible by so-called "contextual keywords" – keywords that only have a meaning in a specific context. The terms In Java 17, "Pattern Matching for switch" was introduced as a preview feature. In combination with this feature, sealed classes allow exhaustion analysis, i.e., the compiler can check whether a switch statement or expression covers all possible cases. Here is a small class hierarchy with a sealed interface as the root: "Pattern Matching for switch" enables code like the following: The compiler recognizes that the object Another advantage is that the compiler will point out the missing Let's extend our class hierarchy with the color green (don't forget to extend the When trying to recompile the So, with sealed class hierarchies, the compiler can help us avoid incomplete switch statements or expressions – a common cause of errors when extending class hierarchies. Sealed classes were introduced by JDK Enhancement Proposal 409 in Java 17. They allow us to protect a class hierarchy from unwanted extensions. For "Pattern Matching for switch", introduced as a preview feature in 17, they also enable completeness analysis. Sealed Classes were developed along with other new language features such as records, switch expressions, text blocks, and pattern matching in Project Amber. If you liked the article, feel free to leave me a comment or share the article using one of the share buttons at the end. Want to be notified when new articles are published? Then click here to sign up for the HappyCoders newsletter.
Starting Point: Example Class Hierarchy
public class Shape { ... } public class Circle extends Shape { ... } public class Rectangle extends Shape { ... } public class Square extends Shape { ... } public class WeirdShape extends Shape { ... } public class TranspRectangle extends Rectangle { ... } public class FilledRectangle extends Rectangle { ... }
WeirdShape class.Why Restrict the Extensibility of a Class Hierarchy?
Sealing the Class Hierarchy – Step by Step
Restricting the Class Hierarchy with "final"
Circle, TranspRectangle, FilledRectangle, and Square as final (remember: WeirdShape should remain the only extendable class).
Shape and Rectangle final because other classes should extend them.Sealing the Class Hierarchy with "sealed" and "permits"
sealed keyword.permits, we list the allowed subclasses.Shape and Rectangle classes as follows: public sealed class Shape permits Circle, Square, Rectangle, WeirdShape { ... } public sealed class Rectangle extends Shape permits TranspRectangle, FilledRectangle { ... }
Shape class may only be extended by the Circle, Square, Rectangle and WeirdShape classes.Rectangle class may only be extended by the TranspRectangle, and FilledRectangle classes.sealed and permits:
Opening the Sealed Class Hierarchy with "non-sealed"
public sealed class Shape permits Circle, Square, Rectangle, WeirdShape { ... } public final class Circle extends Shape { ... } public sealed class Rectangle extends Shape permits TranspRectangle, FilledRectangle { ... } public final class Square extends Shape { ... } public class WeirdShape extends Shape { ... } public final class TranspRectangle extends Rectangle { ... } public final class FilledRectangle extends Rectangle { ... }
$ javac *.java WeirdShape.java:3: error: sealed, non-sealed or final modifiers expected public class WeirdShape extends Shape { ^ sealed, non-sealed, or final.WeirdShape class should be extensible, i.e., the sealing should be opened at this class. Therefore we have to mark this class as non-sealed: public non-sealed class WeirdShape extends Shape { ... }
Particularities
Sealing within a "Compilation Unit"
permits keyword can be omitted if subclasses derived from a sealed class are defined within the same class file ("compilation unit"). These are then considered "implicitly declared permitted subclasses".ChildInSameCompilationUnit is such a subclass; therefore, the permits keyword may be omitted: public sealed class SealedParentWithoutPermits { public final class ChildInSameCompilationUnit extends SealedParentWithoutPermits { // ... } } Local Classes
public class NonSealedParent { public void doSomething () { class LocalChild extends NonSealedParent { // Allowed // ... } // ... } } public sealed class SealedParent { public void doSomething () { class LocalChild extends SealedParent { // Not allowed // ... } // ... } }
instanceof Tests with Sealed Classestrue. If it does not, the compiler reports an "incompatible types" error, such as in the following code: Number n = getNumber(); if (n instanceof String) { // Not allowed // ... } Number object can never be an instance of String. The compiler therefore reports:A and a class B: interface A {} class B {} public boolean isAaB (A a) { return a instanceof B; } true? By defining a class C that inherits from B and implements A: class C extends B implements A {} isAaB(new C()) then returns true.A and allow only AChild as a subclass; we leave class B unchanged: sealed interface A permits AChild {} final class AChild implements A {} class B {} A can never also be an instance of B. Accordingly, the check if (a instanceof B) is from now on acknowledged with the following compiler error:Contextual Keywords
sealed, non-sealed, permits (or yield from switch expressions) raised the following question among JDK developers: What should happen to existing code that uses these keywords as method or variable names?sealed and permits, for example, are such "contextual keywords" and have meaning only in the context of a class definition. In other contexts, they can be used as method or class names. So the following is valid Java code: public void sealed () { int permits = 5; } Completeness Analysis for "Pattern Matching for switch"
public sealed interface Color permits Red, Blue {} public final class Red implements Color {} public final class Blue implements Color {} Color color = getColor(); switch (color) { case Red r -> ... case Blue b -> ... } color can only be an instance of Red or Blue; thus, the switch statement is complete and does not require a default case.switch case if the class hierarchy is extended later.permits list of Color): public sealed interface Color permits Red, Blue, Green {} public final class Red implements Color {} public final class Blue implements Color {} public final class Green implements Color {} switch statement, the compiler aborts with the following error message:
$ javac --enable-preview -source 17 SwitchTest.java SwitchTest.java:6: error: the switch statement does not cover all possible input values switch (color) { ^ Conclusion
hellerlonater1944.blogspot.com
Source: https://www.happycoders.eu/java/sealed-classes/
Post a Comment for "Sealed Class in Java With Example"