现在的位置: 首页 > 综合 > 正文

第17条:要么为继承设计,并提供文档说明,要么就禁止继承

2013年09月01日 ⁄ 综合 ⁄ 共 2413字 ⁄ 字号 评论关闭

首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明他可覆盖(overridable)的方法的自用型(self-use)。
对于每个共有的或受保护的方法或构造器,他的文档必须指明该方法或者构造器调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用的结果又是如何影响后续的处理过程的。更一般的,类必须在文档中说明,在哪些情况下会调用可覆盖的方法。(例如:后台的线程或者静态的初始化可能会调用这个方法)
按惯例,如果方法调用了可覆盖的方法,在他的文档注释的末尾应该包含关于这些调用的描述信息。
好的API应该是描述一个给定的方法做了什么工作,而不是描述他是如何做到的。
类必须通过某种形式提供适当的钩子(hook),以便能够进入他的内部工作流程中,这种实行可以精心选择受保护的(protected)方法。
demo:

AbstractList:
    /**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the ArrayList by {@code (toIndex - fromIndex)}
     * elements.  (If {@code toIndex==fromIndex}, this operation has no
     * effect.)
     *
     * <p>This method is called by the {@code clear} operation on this list
     * and its subLists.  Overriding this method to take advantage of
     * the internals of the list implementation can <i>substantially</i>
     * improve the performance of the {@code clear} operation on this list
     * and its subLists.
     *
     * <p>This implementation gets a list iterator positioned before
     * {@code fromIndex}, and repeatedly calls {@code ListIterator.next}
     * followed by {@code ListIterator.remove} until the entire range has
     * been removed.  <b>Note: if {@code ListIterator.remove} requires linear
     * time, this implementation requires quadratic time.</b>
     *
     * @param fromIndex index of first element to be removed
     * @param toIndex index after last element to be removed
     */
    protected void removeRange(int fromIndex, int toIndex) {
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
            it.next();
            it.remove();
        }
    }
ArrayList:    
    protected void removeRange(int fromIndex, int toIndex) {
		modCount++;
		int numMoved = size - toIndex;
	    System.arraycopy(elementData, toIndex, elementData, fromIndex,
	                         numMoved);
	
		// Let gc do its work
		int newSize = size - (toIndex-fromIndex);
		while (size != newSize)
		    elementData[--size] = null;
    }    

对于为了继承而设计的类,唯一的测试方法就是编写子类。

构造器决不能调用可覆盖的方法:
超类的构造器在子类的构造器之前运行,所以,子类中覆盖版本的方法将会在子类的构造器运行之前就先被调用。如果该覆盖的版本的方法依赖于子类构造器所执行的任何初始化工作,该方法将不会如预期般的执行。
demo:

package cn.partner4java.test;

public class Super {
	public Super() {
		overrideMe();
	}
	public void overrideMe(){
		
	}
}
package cn.partner4java.test;

import java.util.Date;

public final class Sub extends Super {
	private final Date date;
	public Sub() {
		date = new Date();
	}
	public void overrideMe() {
		System.out.println(date);
	}
	public static void main(String[] args) {
		Sub sub = new Sub();
		sub.overrideMe();
//		后台打印:
//		null
//		Wed Mar 21 22:28:49 CST 2012
	}
}

在为了继承而设计类时,Cloneable和Serializable接口出现了特殊的困难。

为了继承而实现类,对这个类会有一些实质性的限制。

对于那些并非为了安全的进行子类化而设计和编写的文档的类,要禁止子类化。
有两种方法禁止之类化:
把类声明为final的;把所有的构造器变为私有的,或者包级私有的,并增加一些共有的静态工厂来替代构造器。

抱歉!评论已关闭.