个人笔记
第四章 ——类和接口
第17条: 要么为继承而设计,并提供文档说明,要么就禁止继承
例子:
在
public abstract class AbstractCollection<E>
中有add和addAll方法
add
public boolean add(E e) {
throw new UnsupportedOperationException();
}
addAll
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))//注意!这里调用了add
modified = true;
return modified;
}
可以看到,AbstractCollection中的实现中addAll中调用了add函数,这种纯粹属于实现细节,不会被导出到API之中,所以,当这个类的用户,继承了AbstractCollection实现了add,那么就会导致其他的Api被污染破坏,出现不可预料的结果,那么,这种情况的补救方法,就是这个类的作者,必须给出明确的说明来作为文档注释导出,用this implements …开头,表明这个实现的特异性,而addAll的文档正是这样做的
/**
* {@inheritDoc}
*
* <p>This implementation iterates over the specified collection, and adds
* each object returned by the iterator to this collection, in turn.
*
* <p>Note that this implementation will throw an
* <tt>UnsupportedOperationException</tt> unless <tt>add</tt> is
* overridden (assuming the specified collection is non-empty).
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws IllegalStateException {@inheritDoc}
*
* @see #add(Object)
*/
不只是这种覆写的方式可能会造成破坏,还有一点,每个子类的构造函数可能会调用super.()或者多个参数来初始化父类的域,那么这个父类的构造中,也就是你设计的类中,就不该对于那些可以被@Override,覆写的方法进行调用,否则也会造成破坏,
如果你做不到这样,或者没有理由让这个类被继承的话,那么请用final修饰去禁止继承它
第18条: 接口优于抽象类
其他的高级语言,比如c++,那用java继承的时候会感觉有些奇怪,在c++世界中是允许多继承的,并且有一个叫做virtual的继承关系,Effective c++中对这个关键字进行了解释
virtual函数的本质是由虚指针和虚表来控制,虚指针指向虚表中的某个函数入口地址,就实现了多态
类的作者将会付出额外的代价,但是好处是强制了类的子类必须这样做,少走弯路,那么java的抽象类(abstract class)也是这样的行为,但是为什么要尽量接口替代抽象类呢,java是单一继承,只能有一个extends 但是可以多个implements 接口,继承代表这层次关系,父子,而接口则代表这个实现类有怎样的行为, 总的有一下几点
现有的类可以很容易被更新,以实现新的接口
层次和平面,单一继承
接口是定义mixin(混合类型) 的理想选择
接口代表着实现了这样的功能和行为,那么很多很多的类型(功能)和基本的类型(功能)组合在一起,就不再需要多个类层次
接口允许我们构造非层次结构的类型框架
对于一个不断修改的类,最好保证原有的行为,那么势必不能随意修改类的实现,最好的方法是在原有的基础上实现(implements)各种接口来安全的扩展,而不是每多一个功能,就得实现子类去扩展,既不优雅,也破坏了类的层次结构
翻看api文档的时候,在util包中发现这样的树形继承结构
标记了一些Abstract开头的类,发现这些常用的容器类,ArrayList,Vector,HashSet等,都有一个这样开头的父类,下面看详细的类声明(ArrayList为例子)
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
然后是ArrayList的父类AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
图片来源blog
底层的容器只需要和Abstract的骨架类打交道,实现一些接口,而抽象的骨架类对一些固定的行为进行实现,为继承树最上层的容器服务
让我想起了,桥接模式,不过桥接是传递实体来服务,这个只是单纯的继承,原文是
通过对你导出的每个重要接口都提供一个抽象的骨架实现类,吧接口和抽象类的优点结合起来,接口的作用仍然是定义类型,但是骨架实现类接管了所有与接口实现相关的工作
骨架实现的美妙之处在于它们为抽象类提供了实现上的帮助,但又不强加"抽象类被用于类型定义时"所持有的严格限制
骨架实现就是为了继承而实现的
第19条: 接口只用于定义类型
java 1.5特性,static导入,使用
import static com.xxx.xxx;
就能使用其中的常量,这样就能避免把一些flag丢入了实现类中,不要使用常量接口,要么成员,要么静态导入