360: | Sealed Classes (Preview)? 封閉類(預覽) |
總結
????????使用密封類和接口增強Java編程語言。密封類和接口限制了哪些其他類或接口可以擴展或實現它們。這是JDK 15中的預覽語言功能。
目標
????????允許類或接口的作者控制負責實現它的代碼。
????????提供一種比訪問修飾符更具聲明性的方式來限制超類的使用。
????????通過支持對模式的詳盡分析,支持模式匹配的未來方向。
動機
????????在Java中,類層次結構允許通過繼承重用代碼:超類的方法可以被許多子類繼承(并因此重用)。然而,類層次結構的目的并不總是重用代碼。有時,它的目的是對領域中存在的各種可能性進行建模,例如圖形庫支持的形狀類型或金融應用程序支持的貸款類型。當以這種方式使用類層次結構時,限制子類集可以簡化建模。
????????例如,在圖形庫中,Shape類的作者可能希望只有特定的類可以擴展Shape,因為該庫的大部分工作都涉及以適當的方式處理每種形狀。作者對處理Shape的已知子類的代碼的清晰度感興趣,對編寫代碼來防御Shape的未知子類不感興趣。在這種情況下,允許任意類擴展Shape,從而繼承其代碼以供重用,并不是我們的目標。不幸的是,Java認為代碼重用始終是一個目標:如果Shape可以擴展,那么它可以擴展任意數量的類。放寬這一假設會有所幫助,這樣作者就可以聲明一個不允許任意類擴展的類層次結構。在這樣一個封閉的類層次結構中,代碼重用仍然是可能的,但在更大的范圍內是不可能的。
????????Java開發人員熟悉限制子類集的想法,因為它經常出現在API設計中。該語言在這方面提供了有限的工具:要么將類設為final,這樣它就沒有子類,要么將類或其構造函數包設為私有,這樣它只能在同一個包中有子類。JDK中出現了一個包私有超類的示例:
package java.lang;abstract class AbstractStringBuilder {...}
public final class StringBuffer extends AbstractStringBuilder {...}
public final class StringBuilder extends AbstractStringBuilder {...}
????????當目標是代碼重用時,包私有方法很有用,例如AbstractStringBuilder的子類共享其代碼進行追加。然而,當目標是對替代方案進行建模時,這種方法是無用的,因為用戶代碼無法訪問關鍵抽象(超類)以進行切換。不允許用戶在不擴展超類的情況下訪問超類是不可能的。(即使在聲明Shape及其子類的圖形庫中,如果只有一個包可以訪問Shape,那也會很不幸。)
總之,超類應該可以廣泛訪問(因為它代表了用戶的一個重要抽象),但不能廣泛擴展(因為它的子類應該僅限于作者已知的子類)。這樣的超類應該能夠表示它是與一組給定的子類共同開發的,既可以為讀者記錄意圖,也可以允許Java編譯器執行。同時,超類不應過度約束其子類,例如強制它們為最終類或阻止它們定義自己的狀態。
示例
public abstract sealed class Shape permits Circle, Rectangle, Square {
}
public non-sealed class Square extends Shape {void side() {System.out.println("邊長");}
}
public non-sealed class Rectangle implements Shape {void length() {System.out.println("長度");}
}
public non-sealed class Circle extends Shape {void center() {System.out.println("圓心");}
}
public static void fun(Shape shape){switch (shape){case Circle c -> c.center();case Rectangle r -> r.length();case Square s -> s.side();default -> throw new IllegalStateException("Unexpected value: " + shape);}}public static void main(String[] args) {Shape s1= new Circle();Shape s2= new Rectangle();Shape s3= new Square();fun(s1);fun(s2);fun(s3);}
注意
使用sealed關鍵字聲明封閉類或封閉接口
使用permits關鍵字允許聲明哪個類或接口可以成為其子類或子接口
sealed修飾過的類或接口必須有類繼承或實現
sealed也可以修飾接口interface
繼承或者實現被sealed修飾過的類或接口,必須用sealed(密封),final(最終),non-sealed(非密封)修飾