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