Java類(接口)的新類型-密封類
密封類是Java 17正式支持的一個新特性,它讓Java中類的繼承可以更加細粒度的進行控制。今天就來認識一下這個新的功能。
密封類
在以往的Java類繼承中,Java類的繼承控制非常有限,僅能通過final關鍵字和訪問控制符來控制類的繼承。例如final類無法被集成;包私有類僅僅只能在該包下進行繼承。
這顯然是不夠的。如果一個功能只允許出現在Phone和Pad上,不允許出現在Computer上。如果不對該功能的繼承實現進行限制,開發人員將很容易濫用該功能的實現類,錯誤地重用一些代碼。這就是密封類產生的原因。
密封類的聲明
密封類不僅僅可以是類,也可以是接口。文章中的密封類為統稱
密封類(接口)可以明確哪些類和接口可以對其擴展或實現。你可以通過sealed修飾符來表明某個類是密封類。但是下面是一個錯誤的密封類聲明:
- /**
- * 這是一個錯誤的示范
- */
- public sealed interface SealedService {
- void doSomething();
- }
密封類(接口)在聲明的時候必須明確可繼承(實現)的范圍,所以上面的寫法是錯誤的。必須用permits子句指定允許擴展密封類的類,而且permits關鍵字位于extends或者implements之后。
簡而言之,密封類明確了哪些其他類(或接口)可以擴展它們。
下面是正確的寫法:
- /**
- * 這是一個正確的示范,明確了可繼承的子類為{@link SealedServiceImpl}
- * 該密封類接口同時實現了{@link SuperService}
- */
- public sealed interface SealedService extends SuperService permits SealedServiceImpl {
- void doSomething();
- }
- /**
- * 密封類子類
- */
- public final class SealedServiceImpl implements SealedService {
- @Override
- public void doSomething() {
- System.out.println("這是一個密封類子類");
- }
- }
密封類子類的類型
在上面示例中,密封類(接口)的實現類用了final關鍵字標記,當然密封類的實現類還可以是密封類:
- /**
- * 密封類子類
- */
- public sealed class SealedServiceImpl implements SealedService permits SonService {
- @Override
- public void doSomething() {
- System.out.println("這是一個密封類子類");
- }
- }
- public final class SonService extends SealedServiceImpl {
- }
那么難道密封類(接口)的子類只能是final類或者密封類,就不能再擴展了?答案是否定的,只需要使用關鍵字non-sealed顯式聲明密封類的繼承實現為非密封類就可以繼續擴展了。
- public non-sealed class SealedServiceImpl implements SealedService {
- @Override
- public void doSomething() {
- }
- /**
- * 用{@code non-sealed}聲明非密封類,就可以繼續擴展了
- */
- static class NonSealedExtend extends SealedServiceImpl {
- }
- }
總結一下,密封類的子類要么是final Class;要么是sealed Class;要么是non-sealed Class。
permits 聲明的類必須是直接子類
密封類permits關鍵字聲明的子類必須是直接實現類,為了證明這一點我們這樣寫:
- /**
- * 錯誤的示范
- */
- public sealed interface SealedService extends SuperService permits SealedServiceImpl, SonService {
- void doSomething();
- }
- public sealed class SealedServiceImpl implements SealedService permits SonService {
- @Override
- public void doSomething() {
- System.out.println("這是一個密封類子類");
- }
- }
- public final class SonService extends SealedServiceImpl {
- }
我使用SonService間接實現了SealedService,結果報錯,報錯信息要求必須是直接的繼承關系。
錯誤的密封類繼承實現
從上圖可以看出SonService并非直接實現SealedService,這樣會打破密封類的規則,所以無法編譯通過。
密封類中permits關鍵字聲明的子類必須是直接子類,不可間接實現。
密封類不支持匿名類和函數式接口
由于密封類必須明確繼承實現關系,所以它不支持匿名類。
- /**
- * 密封類無法使用匿名類
- *
- * @return the sealed service
- */
- public SealedService sealedService(){
- // 提示 Anonymous classes must not extend sealed classes
- return new SealedService() {
- @Override
- public void doSomething() {
- }
- };
- }
同樣也不支持函數式接口:
- /**
- * 錯誤的示范
- */
- @FunctionalInterface
- public sealed interface SealedService permits SealedServiceImpl {
- void doSomething();
- }
總結
密封類已經在Java 17中正式轉正,這也是Java 17的非常重要的特性之一。對于需要細粒度控制繼承關系的場景來說是非常有用的。