Java 17推出的新特性Sealed Classes經歷了2個Preview版本(JDK 15中的JEP 360、JDK 16中的JEP 397),最終定稿於JDK 17中的JEP 409。Sealed Classes有兩種主流翻譯:密封類、封閉類。個人喜歡前者多一些,所以在本文中都稱為密封類。其實Sealed Classes的其他許多語言中並不是什麼新鮮事物,C#、Scala等高階語言中都有類似的名稱,但意義和作用各不相同。下面就來一起認識一下Java 17中的Sealed Classes。
密封類的作用
在面嚮物件語言中,我們可以通過繼承(extend)來實現類的能力複用、擴充套件與增強。但有的時候,有些能力我們不希望被繼承了去做一些不可預知的擴充套件。所以,我們需要對繼承關係有一些限制的控制手段。而密封類的作用就是限制類的繼承。
已有的限制手段
對於繼承能力的控制,Java很早就已經有一些了,主要是這兩種方式:
final
修飾類,這樣類就無法被繼承了package-private
類(非public類),可以控制只能被同一個包下的類繼承
但很顯然,這兩種限制方式的粒度都非常粗,如果有更精細化的限制需求的話,是很難實現的。
新特性:密封類
為了進一步增強限制能力,Java 17中的密封類增加了幾個重要關鍵詞:
sealed
:修飾類/介面,用來描述這個類/介面為密封類/介面non-sealed
:修飾類/介面,用來描述這個類/介面為非密封類/介面permits
:用在extends
和implements
之後,指定可以繼承或實現的類
下面我們通過一個例子來理解這幾個關鍵詞的用法,更多Java新特性,歡迎關注Java前沿專欄,文件形式看Java新特性,閱讀學習體驗更佳,持續更新,收藏儲存!
假設我們要設計一個遊戲,這個遊戲給使用者選擇的英雄種類分為三大類:
- 坦克
- 輸出
- 輔助
每個種類下又有各種不同的具體英雄。所以,從我們傳統的面向設計思路,會這樣來建立:
// 英雄基類
public class Hero {
}
// 坦克英雄的抽象
public class TankHero extends Hero {
}
// 輸出英雄的抽象
public class AttackHero extends Hero {
}
// 輔助英雄的抽象
public class SupportHero extends Hero {
}
// 坦克英雄:阿利斯塔
public class Alistar extends TankHero {
}
// 輸出英雄:伊澤瑞爾
public class Ezreal extends AttackHero {
}
// 輔助英雄:索拉卡
public class Soraka extends SupportHero {
}
整體結構有三層,具體如下圖所示:
- 第一層:Hero是所有英雄的基類,定義英雄的基礎屬性
- 第二層:按英雄的分類的三個不同抽象,定義同類英雄的公共屬性
- 第三層:具體英雄的定義
這個時候,為了避免開發人員在建立新英雄的時候,搞亂這樣的三層結構。就可以通過引入密封類的特性來做限制。
假設我們希望第一、第二層是穩定的,對於第二層英雄種類的抽象不允許再增加,此時我們就可以這樣寫:
public sealed class Hero permits TankHero, AttackHero, SupportHero {
}
通過sealed
關鍵詞和permitspermits
關鍵來定義Hero是一個需要密封的類,並且它的子類只允許為TankHero
, AttackHero
, SupportHero
這三個。
完成這個改造之後,我們會發現TankHero
, AttackHero
, SupportHero
這三個類開始報錯了,具體錯誤如下:
sealed, non-sealed or final modifiers expected
這是因為父類Hero被sealed
修飾之後,sealed
的密封要求被傳遞過來,此時子類就必須在sealed
、non-sealed
、final
之間選擇一個定義,它們分別代表:
sealed
:繼續延續密封類特性,可以繼續指定繼承的類,並傳遞密封定義給子類non-sealed
:宣告這個類為非密封類,可以被任意繼承final
:不允許繼承
根據上面的假設需求,第一、第二層穩定,允許第三層具體英雄角色可以後期不斷增加新英雄,所以三類抽象英雄的定義可以這樣編寫:
public non-sealed class TankHero extends Hero {
}
而對於第三層的英雄角色,已經是最後的具體實現,則可以使用final定義來阻斷後續的繼承關係,比如這樣:
public final class Ezreal extends AttackHero {
}
通過這樣的設定,這三層英雄的結構中第一第二層就得到了比較好的保護。
好了,今天的分享就到這裡!如果您學習過程中如遇困難?可以加入我們超高質量的技術交流群,參與交流與討論,更好的學習與進步!另外,不要走開,關注我!持續更新Java新特性專欄,文件形式看Java新特性,閱讀學習體驗更佳!
歡迎關注我的公眾號:程式猿DD。第一時間瞭解前沿行業訊息、分享深度技術乾貨、獲取優質學習資源