继承需要提供文档

若要设计继承,则提供文档说明,否则禁止继承

概述

对于那些不是为了继承,并且没有良好说明文档的外来类来说,去继承产生子类是非常危险的。

如何设计文档

  1. 必须在这个类的文档里为可覆盖方法说明它的自用性

对于每个公有方法或者受保护方法,文档里必须指明这个方法调用了哪些可覆盖方法,是以什么顺序调用的,每个调用的结果是如何影响接下来的处理过程。

调用可覆盖方法的方法应该在注释末尾包含这些调用的描述,这可以由Javadoc标签@implSpec生成。

以下为ava.util.AbstractCollection的remove方法的规范:

Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that Objects.equals(o, e), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call).

Implementation Requirements:This implementation iterates over the collection looking for the specified element. If it finds the element, it removes the element from the collection using the iterator’s remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection’s iterator method does not implement the remove method and this collection contains the specified object.

  1. 父类必须以某种形式提供能够进入到其内部运转的hook

这里可以选择一个受保护的方法、或者是受保护域。

约束条件

若一个类允许被继承,有几点约束需要遵守:

  1. 构造器一定不能调用可覆盖方法

无论是直接还是间接调用,因为父类构造器在子类构造器之前运行,所以子类的覆盖方法会在子类构造器运行之前被调用。如果覆盖后的方法依赖于子类构造器的任意初始化操作,那么找哥哥方法很可能会产生非预期的行为。

  1. 如何使用Cloneable和Serializable接口

对于Cloneable接口,由于clone方法和readObject方法的行为类似构造器,所以类似上面的约束依然存在。

而对于Serializable接口,而且这个类拥有readResolve方法或writeReplace方法,你一定要把readResolve方法或writeReplace方法设为受保护的,而不是私有的。

总结

设计一个用来被继承的类是不容易的,我们必须在文档里说明该类的自用模式,并且为了让别人编写有效的子类,我们也需要导出一个或多个受保护的方法。

除非我们知道某个类的确是要被子类化,否则最好将声明为final或者保证其没有可访问的构造器来禁止该类被继承。