美文网首页技术干货Android知识程序员
实现不可变类如何禁止子类化?

实现不可变类如何禁止子类化?

作者: 猴子007 | 来源:发表于2017-09-20 19:18 被阅读0次

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

JDK版本:oracle java 1.8.0_102

如何禁止子类化

常用姿势

最简单的手段是将类声明为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. 语法优于约定

本文链接:实现不可变类时如何禁止子类化?
作者:猴子007
出处:https://monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名及链接。

相关文章

  • 实现不可变类如何禁止子类化?

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

  • String、StringBuffer、StringBuilde

    String String是不可变的类,即final 类,String底层使用final Char[] 来实现,不...

  • Java学习笔记2

    Singleton / 不可变类 / 缓存不可变类 的实现 Singleton(单例类) 单例类用的地方很多,如果...

  • ios轻量级app开机引导界面

    为了不侵染业务代码,本人思考许久,决定采用子类化UIWindow的方式来实现该功能。 实现思路: 子类化UIWin...

  • Object-C 学习笔记

    类的申明放在 .h 文件中,类的实现放在 .m 文件中 类分为可变类、不可变类2.1. 必须在不可变对象创建时,设...

  • Android Module之间数据传递

    方法一:使用接口回调 (1)在子module创建回调接口(参数可变) (2)在子module 实现类设置接口回调 ...

  • Template Method 模板方法模式

    设计原则学习笔记 设计模式学习笔记 作用 将可变的和不可变的代码分离,可变的代码由子类实现,不可变代码由父类实现...

  • 关于Java核心类String的几个问题

    1. 前言 最近看到几个有趣的关于Java核心类String的问题。 String类是如何实现其不可变的特性的,设...

  • Java不可变类

    1. 什么是不可变类? 2. 如何创建不可变类? 2.1通过 Collections.unmodifiable**...

  • Spring常见拓展总结

    通常,应用程序开发人员不需要子类化ApplicationContext实现类。相反,可以通过插入特殊集成接口的实现...

网友评论

    本文标题:实现不可变类如何禁止子类化?

    本文链接:https://www.haomeiwen.com/subject/seaosxtx.html