詳解 Java 17中的新特性:「密封類」

java鬥帝之路 發佈 2022-05-07T15:32:55.084116+00:00

Java 17推出的新特性Sealed Classes經歷了2個Preview版本(JDK 15中的JEP 360、JDK 16中的JEP 397),最終定稿於JDK 17中的JEP 409。Sealed Classes有兩種主流翻譯:密封類、封閉類。

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很早就已經有一些了,主要是這兩種方式:

  1. final修飾類,這樣類就無法被繼承了
  2. package-private類,可以控制只能被同一個包下的類繼承

但很顯然,這兩種限制方式的力度都非常粗,如果有更精細化的限制需求的話,是很難實現的。

新手段:密封類

為了進一步增強限制能力,Java 17中的密封類增加了幾個重要關鍵詞:

  • sealed:修飾類/接口,用來描述這個類/接口為密封類/接口
  • non-sealed:修飾類/接口,用來描述這個類/接口為非密封類/接口
  • permits:用在extendsimplements之後,指定可以繼承或實現的類

下面我們通過一個例子來理解這幾個關鍵詞的用法,更多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的密封要求被傳遞過來,此時子類就必須在sealednon-sealedfinal之間選擇一個定義,它們分別代表:

  • sealed:繼續延續密封類特性,可以繼續指定繼承的類,並傳遞密封定義給子類
  • non-sealed:聲明這個類為非密封類,可以被任意繼承
  • final:不允許繼承

根據上面的假設需求,第一、第二層穩定,允許第三層具體英雄角色可以後期不斷增加新英雄,所以三類抽象英雄的定義可以這樣編寫:

public non-sealed class TankHero extends Hero {

}

而對於第三層的英雄角色,已經是最後的具體實現,則可以使用final定義來阻斷後續的繼承關係,比如這樣:

public final class Ezreal extends AttackHero {

}

通過這樣的設置,這三層英雄的結構中第一第二層就得到了比較好的保護。


來源:公眾號——程序猿DD

關鍵字: