第17条:要么为继承而设计,并提供文档说明,要么就禁止继承
首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明他可覆盖(overridable)的方法的自用型(self-use)。对于每个共有的或受保护的方法或构造器,他的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的。更一般的,类必须在文档中说明,在哪些情况下会调用可覆盖的方法。
按惯例,如果方法调用了可覆盖的方法,在他的文档注释的末尾应该包含关于这些调用的描述信息。
/**
* 17 要么为继承而设计,并提供文档说明,要么就禁止继承
*/
public class SubAndSuper extends Super{
private final Date date;
public SubAndSuper() {
this.date = new Date();
}
@Override
public void overrideMe() {
System.out.println(date);
}
public static void main(String [] args){
SubAndSuper ss = new SubAndSuper();
/*返回结果
null
Fri Mar 15 18:40:55 CST 2019
*/
ss.overrideMe();
}
}
/*
* 为了能编写有效的子类,父类必须通过某种形式提供适当的钩子(hook),一遍能够进入到它的内部工作流中,可以是受保护的方法或者受保护的域
* 对于为了继承而设计的类,唯一的测试方法就是编写子类,而且必须全面测试,最好有3个子类进行
* 为了允许继承,构造器决不能调用可以覆盖的方法。注:这块经常出现面试题,本示例就是典型的构造器内调用可以覆盖的方法
* 如果基类实现了Cloneable或者Serializable,注意,clone方法 和 readObject方法都类似于构造器,所以他们不可以调用可以被覆盖的方法,不管是直接调用还是间接调用。
*
* 对于那些并非废了安全地进行子类化而设计和编写文档的类,要禁止子类化。方式 定义为final类,或者 私有化所有构造器并提供静态工厂代替构造器(静态方法返回新建的本类)
* */
class Super{
public Super() {
overrideMe();
}
public void overrideMe(){
}
}
为了继承而进行设计不仅仅涉及自用模式的文档设计。为了使程序员能够编写出更加有效的子类,而无需随不必要的痛苦,类必须通过某种形式提供适当的钩子,以便能够进入到它的内部工作流程中,这种形式可以是精心选择的受保护的方法,也可以是受保护的域,后者比较少见。
对于为了继承而设计的类,唯一的测试方法就是编写子类。
构造器决不能调用可覆盖的方法:
超类的构造器在子类的构造器之前运行,所以,子类中覆盖版本的方法将会在子类的构造器运行之前就先被调用。如果该覆盖的版本的方法依赖于子类构造器所执行的任何初始化工作,该方法将不会如预期般的执行。
对于那些并非为了安全的进行子类化而设计和编写的文档的类,要禁止子类化。
如果具体的类没有实现标准的接口,那么禁止继承可能会给有些程序员带来不便,如果认为必须允许从这样的类继承,一种合理的办法就是确保这个类永远不会调用它的任何可覆盖的方法,并在文档中说明这一点。
有两种方法禁止之类化:
1 把类声明为final的;
2 把所有的构造器变为私有的,或者包级私有的,并增加一些共有的静态工厂来替代构造器。