Java 15 新特性—密封的类和接口

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

引言

继承,作为面向对象语言的三大特性之一,我相信没有小伙伴不知道吧?在工作过程中我们也经常使用继承,我么知道子类继承父类,可以重写父类的方法,编写自己独特的属性与行为,任何依赖父类的业务,子类都可以替换掉它。这种情况再绝大多数情况下是非常有价值的,除了少数情况。

这少数情况就是:我们需要继承,但是我们又期望能够限制它继承的能力。

是不是很矛盾?我们以加密算法为样例来说明。

我们知道在加密算法的设计中,确保算法的实现不被随意修改或扩展是至关重要的。加入你负责一个内部通信框架,报文都需要加解密。你设计了一套通用的加密算法,其中包含几种标准的加密算法,如AES、DES和RSA,如下:

public abstract class Encryptor {
    public abstract byte[] encrypt(byte[] data);
    public abstract byte[] decrypt(byte[] data);
}

public class AesEncryptor extends Encryptor{
    // 省略代码
}

public class DesEncryptor extends Encryptor{
    // 省略代码
}

public class RsaEncryptor extends Encryptor{
    // 省略代码
}


你提供给团队使用,但是有一些头铁的团队觉得加密没有必要,或者自己为了 KPI 实现了一套自己的加密算法,通过继承的方式来绕过你框架的加密算法,首先在代码上是检测不出来有问题的,但是他有安全隐患。

怎么杜绝这种情况呢?在 Java 15 之前我们有如下方案:

  1. 定义 final 修饰类,这样类就无法被继承了。
  2. package-private类(非public类),可以控制只能被同一个包下的类继承
  3. 依赖沟通与实际的非代码的约束

方案 3 不靠谱,方案 1 和 2 限制的粒度都非常粗,如果我们有更精细化的限制需求的话,就是很难实现了。

密封类

为了进一步增强继承的限制能力,Java 15 引入密封类来精确控制类的继承问题 ,目前版本为预览特性。

什么是密封类

密封类的主要目的是提供一种更加精确地控制类继承的方法,通过这种方式,类的设计者可以指定一个类它能够被哪些类继承,它增强了类的封装性和安全性。由于密封类限制了类的继承,所以它使得代码更加可预测和易于维护。

  • 密封类用 sealed 修饰,则它的所有子类都必须在同一个模块或者包内,并且这些子类必须被显式地声明为该密封类的直接子类。
  • 密封类的子类可以被声明为non-sealed(非密封的)或final(最终的)。non-sealed的子类可以被进一步继承,而final的子类则不能。
  • 密封类使用 permits 来指定它的子类。

与密封类相似,接口也可以被声明为密封的,从而限制哪些其他接口或类可以实现或扩展它。

示例代码

假设我们要设计一个游戏,该游戏有三类英雄:战士、法师、射手。按照传统的继承方式就很简单了,大明哥就不演示了,这里我们直接看如何利用密封类来控制我们的设计思路。

游戏初期,我们游戏有且只有这三类英雄,我们这样设计:

// 英雄基类
public class Hero {
}

// 战士
public class Warrior extends Hero{
}

// 法师
public class Archer extends Hero{
}

// 射手
public class Mage extends Hero{
}

类的结构如下:

开始时,这种写法是没有问题的。但是随着版本的迭代, 游戏英雄需要改造,整体还是分为战士、法师、射手三类,每个类别下面有三个英雄:

  • 战士:烈焰勇士(FlameWarrior)、铁甲巨人(IronTitan)、疾风剑士(Swiftblade)
  • 射手:寒冰射手(IceArcher)、风暴射手(StormShooter)、影夜射手(ShadowNightArcher)
  • 法师:元素法师(ElementMage)、虚空巫师(VoidSorcerer)、光明祭司(LightPriest)

由于英雄类型只有三类,所以我们的 Hero 一定要限制,为什么?因为团队技术能力有高有低,总有不怕死的人会用寒冰射手继承 Hero。所以我们代码如下。

  • Hero 为基类,只允许 Warrior 、ArcheMage 继承。所以 Hero 需要用 sealed 修饰,同时利用 permits 来指定子类为:Warrior 、ArcheMage,如下:
// 英雄基类,限制子类为:战士(Warrior)、射手(Mage)、法师(Archer)
public sealed class Hero permits Warrior,Mage,Archer {
}
  • 二层英雄类型,他们还需要子类继承,我们同样不希望他的继承被扩散,所以继续使用 sealed 修饰
// 战士,限制子类为:烈焰勇士(FlameWarrior)、铁甲巨人(IronTitan)、疾风剑士(Swiftblade)
public sealed class Warrior extends Hero permits FlameWarrior,IronTitan,Swiftblade{
}

// 射手,限制子类为:寒冰射手(IceArcher)、风暴射手(StormShooter)、影夜射手(ShadowNightArcher)
public sealed class Mage extends Hero permits IceArcher,StormShooter,ShadowNightArcher{
}

// 法师,限制子类为:元素法师(ElementMage)、虚空巫师(VoidSorcerer)、光明祭司(LightPriest)
public sealed class Archer extends Hero permits ElementMage,VoidSorcerer,LightPriest{
}

第二层结构就很稳定了。

  • 三层为具体英雄,他们继承二层的英雄类型,使用 extends 继承即可,同时需要表示为non-sealedfinal,由于我们不希望类再往下了,所以定义为 final
public final class FlameWarrior extends Warrior{
}

public final class IronTitan extends Warrior{
}

public final class Swiftblade extends Warrior{
}

// 省略其他 6 个类

整体结构如下:

结构是一个非常稳定的三层结构,没有任何类能够继承里面的任一一个类。通过这样的设计,就将这三层保护得非常好了。

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值