实现不可变类时要求禁止子类化。本文先讲禁止子类化的方式,最后解释为什么要禁止子类化。
如何禁止子类化
常用姿势
最简单的手段是将类声明为final,如String、Integer等常用的值类。但这样缺乏灵活性:不仅禁止了用户的子类化,开发者也无法利用子类化减少编码工作。
尽管这种手段完全没有变通,却是我们使用最多的一种。只有你需要上述灵活性的时候,再去考虑下述方式。
不常用但你需要掌握的姿势
还有一种不常用,但更灵活的方法:静态工厂方法+私有构造器。
完全禁止子类化(效果类似于final修饰)
如果希望Parent3完全不可子类化,除了用final修饰Parent3以外,还可以用private修饰其所有构造方法,这样Child3因无法调用父类的构造方法,也无法通过编译:
|
|
通过静态工厂方法构造Parent3的实例。
更灵活的子类化限制
但是,如果放松Parent3构造方法的访问权限, 我们还能得到更灵活的子类化限制。比如允许包级私有的子类化:
|
|
需要注意的是,Java的覆写机制要求覆写方法(Child5())的权限不低于被覆写方法(Parent5())。这造成了一种危险:如果将Child5()声明为public,那么Child5变得可子类化,间接实现了Parent5的子类化。
PS:以上实现不能定义为内部类,如果有疑问,你需要回忆private的语义。
为什么要禁止子类化
如果允许子类化,在发生多态的情况下,通过覆写子类的访问器,可以让子类冒充父类,让父类“看起来”是可变的:
|
|
总结
上述方式从语法层面保证了不可变类的禁止子类化。尽管我们能通过其他办法在多态访问时判断当前对象是父类还是子类的实例,但哪种方式更恰当呢?显然是前者,两个理由:
- 用合适的方式做合适的事
- 语法优于约定