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類,可以控制只能被同一個包下的類繼承
但很顯然,這兩種限制方式的力度都非常粗,如果有更精細化的限制需求的話,是很難實現的。
新手段:密封類
為了進一步增強限制能力,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 {
}
通過這樣的設置,這三層英雄的結構中第一第二層就得到了比較好的保護。
來源:公眾號——程序猿DD