A sealed class is declared using the modifier sealed and the optional permits clause in the class header. The permits clause is optional if a sealed class and its permitted direct subclasses are declared in the same compilation unit (p. 317). If a class specifies the permits clause, the class must be declared sealed. The permits clause, if specified, must appear after any extends and implements clauses in the class header.
The superclass Book is declared sealed and explicitly specifies a permits clause with a list of its permitted direct subclasses, as shown at (1) below. Keep in mind that since these subclasses are public, they are declared in separate compilation units, but belong to the same package.
public abstract sealed class Book permits PrintedBook, Ebook, Audiobook {/*(1)*/}
Each permitted direct subclass of a sealed class is bound by the following contract:
- Each permitted direct subclass must extend its direct sealed superclass. Note that the sealing relationship is defined between two consecutive levels in the inheritance hierarchy: between a superclass and its direct subclasses. This is also the case for the permitted direct subclasses of the Book superclass:
public non-sealed class PrintedBook extends Book {/*…*/}
public final class Ebook extends Book {/*…*/}
public final class Audiobook extends Book {/*…*/}
- Each permitted direct subclass must be declared either sealed, non-sealed, or final. This avoids any unwanted subclassing to the inheritance hierarchy of a sealed class.
A permitted direct subclass that is declared final cannot be extended, as in the case of a final normal class. This implies that the inheritance hierarchy of a final permitted subclass cannot be extended. The permitted direct subclasses Ebook and Audiobook are declared final. Any attempt to extend them would result in a compile-time error.
class EComicBook extends Ebook {} // Compile-time error!
A permitted direct subclass that is declared non-sealed can be freely extended. This implies that the inheritance hierarchy of a non-sealed permitted subclass can be extended. Note that a class cannot be declared non-sealed unless it is a permitted direct subclass of a direct sealed superclass.
The permitted direct subclass PrintedBook is declared non-sealed. Client code can readily extend this class:
class Hardcover extends PrintedBook {}
class Paperback extends PrintedBook {}
A sealed permitted direct subclass applies sealing to its own permitted direct subclasses, just like its direct sealed superclass. This implies that the inheritance hierarchy of a sealed permitted subclass can be extended, but in a restricted way. For example, if the permitted subclass PrintedBook was declared sealed, its permitted direct subclasses would be subject to the same rules for declaring permitted subclasses, as shown below:
public sealed class PrintedBook extends Book permits Hardcover, Paperback {}
final class Hardcover extends PrintedBook {}
final class Paperback extends PrintedBook {}
- Each permitted direct subclass must be accessible by the direct sealed superclass.
If the sealed superclass is in a named module, the permitted subclasses must also be in the same module (Chapter 19, p. 1161). Note that this does not mean that they have to be in the same package in the named module. Their fully qualified names can be used in the permits clause to access the subclasses in their respective packages (§6.3, p. 326).
If the sealed superclass is in the unnamed module, the permitted subclasses must all be in either the same named package or the unnamed package as the sealed superclass. In Example 5.32, the sealed superclass Book is in a named package (but in the unnamed module), as are the permitted subclasses, specified using the package statement (§6.3, p. 326).
Example 5.32 Declaring sealed, non-sealed, and final Classes
package signedandsealed;
public abstract sealed class Book permits PrintedBook, Ebook, Audiobook { // (1)
private String isbn;
protected Book(String isbn) {
this.isbn = isbn;
}
public String getIsbn() { return this.isbn; }
}
package signedandsealed;
public non-sealed class PrintedBook extends Book { // (2)
private int pageCount;
protected PrintedBook(String isbn, int pageCount) {
super(isbn);
this.pageCount = pageCount;
}
public int getPageCount() { return this.pageCount; }
}
package signedandsealed;
public final class Ebook extends Book { // (3)
private String format;
public Ebook(String isbn, String format) {
super(isbn);
this.format = format;
}
public String getFormat() { return this.format; }
}
Click here to view code image package signedandsealed;
public final class Audiobook extends Book { // (4)
private String narrator;
private double length;
public Audiobook(String isbn, String narrator, double length) {
super(isbn);
this.narrator = narrator;
this.length = length;
}
public String getNarrator() { return this.narrator; }
public double getLength() { return this.length; }
}
Leave a Reply