博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现不可变类如何禁止子类化?
阅读量:6331 次
发布时间:2019-06-22

本文共 1687 字,大约阅读时间需要 5 分钟。

实现不可变类时要求禁止子类化。本文先讲禁止子类化的方式,最后解释为什么要禁止子类化。

如何禁止子类化

常用姿势

最简单的手段是将类声明为final,如String、Integer等常用的值类。但这样缺乏灵活性:不仅禁止了用户的子类化,开发者也无法利用子类化减少编码工作。

尽管这种手段完全没有变通,却是我们使用最多的一种。只有你需要上述灵活性的时候,再去考虑下述方式。

不常用但你需要掌握的姿势

还有一种不常用,但更灵活的方法:静态工厂方法+私有构造器。

完全禁止子类化(效果类似于final修饰)

如果希望Parent3完全不可子类化,除了用final修饰Parent3以外,还可以用private修饰其所有构造方法,这样Child3因无法调用父类的构造方法,也无法通过编译:

public class Parent3 {  private Parent3() {  }}// 无法调用父类的构造方法,因此无法通过编译,即Parent3无法子类化public class Child3 extends Parent3 {  private Child3() {    super();  }}复制代码

通过静态工厂方法构造Parent3的实例。

更灵活的子类化限制

但是,如果放松Parent3构造方法的访问权限, 我们还能得到更灵活的子类化限制。比如允许包级私有的子类化:

public class Parent5 {  Parent5() {  }}// 只要Child5与Parent5定义在同一个包内,就可以子类化public class Child5 extends Parent5 {  Child5() {    super();  }}复制代码

需要注意的是,Java的覆写机制要求覆写方法(Child5())的权限不低于被覆写方法(Parent5())。这造成了一种危险:如果将Child5()声明为public,那么Child5变得可子类化,间接实现了Parent5的子类化

PS:以上实现不能定义为内部类,如果有疑问,你需要回忆private的语义。

为什么要禁止子类化

如果允许子类化,在发生多态的情况下,通过覆写子类的访问器,可以让子类冒充父类,让父类“看起来”是可变的:

public class ImmutableParent {    private final int imVal;    public ImmutableParent(int imVal) {      this.imVal = imVal;    }    public int getImVal() {      return imVal;    }  }  …  public class MutableChild extends ImmutableParent {    private int mVal;    public MutableChild(int imVal) {      super(imVal);      mVal = imVal;    }    // 覆写父类的不可变字段 imVal 的访问器, 发生多态时子类实例就能伪装成父类实例, 让用户访问可变字段 mVal    @Override    public int getImVal() {      return mVal;    }    // 而伪装者撕下面具时(改用子类引用), 就能随意修改可变字段 mVal    public void setImVal(int mVal) {      this.mVal = mVal;    }  }复制代码

总结

上述方式从语法层面保证了不可变类的禁止子类化。尽管我们能通过其他办法在多态访问时判断当前对象是父类还是子类的实例,但哪种方式更恰当呢?显然是前者,两个理由:

  1. 用合适的方式做合适的事
  2. 语法优于约定

本文链接:

作者:
出处:
本文基于国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。

你可能感兴趣的文章
思考驱动创新,创新驱动发展:基于假设(Assumption)的思考技术
查看>>
Exchange 全球通讯录导入基于POP3模式的Outlook
查看>>
HSRP(热备份协议)
查看>>
磁盘Mbr浅谈
查看>>
Kafka0.8性能测试报告
查看>>
提醒:涉及数据库这类的东西一定需要注意长短链接问题
查看>>
Windows Server 2012正式版RDS系列⒄
查看>>
Cocos Studio 2.3.2不再支持直接导入PSD文件
查看>>
Lync 小技巧-23-企业版与标准版-安装-对比
查看>>
Redis数据持久化
查看>>
Windows Phone 7 XNA开发之关于游戏组件
查看>>
方韩大战与无边界创新
查看>>
苹果就剩下虚荣了
查看>>
SFB 项目经验-18-三台前端服务器同时停止两台后-前端服务启不来
查看>>
我的友情链接
查看>>
Lync 小技巧-5-当前已暂停共享
查看>>
3000字长文“狂喷”电商行业刷单焦虑
查看>>
裂变大神:营销裂变三步法
查看>>
我的第三本书<<精通软件性能测试与LoadRunner最佳实战>>
查看>>
集成服务入门(实验2)使用复制数据库向导
查看>>