EffectiveJava——类与接口3

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

随意的基层某个没有文档说明的类是非常危险地,所以要么为继承而设计,并提供文档说明,要么就禁止继承,那么对于专门为了继承而设计的类并具有良好的说明文档的类而言,有意味着什么呢?

首先类的文档必须精确的描述覆盖每个方法所带来的影响,关于程序文档有句格言好的文档应该描述一个给定的方法做了什么工作,而不是描述它是如何做到的

为了使程序员能够编写出更加有效的子类,而无需承受不必要的痛苦,类必须通过某种形式提供适当的钩子(hook),以便能够进入到他的内部工作流程中,这种形式可以是精心选择的protected方法或者是protected的域。

对于为了继承而设计的类,唯一的测试方法就是编写子类。3个子类通常足以测试一个可扩展的类。

为了允许继承,类还必须遵守其他的一些约束,构造器绝不能调用可被覆盖的方法。无论是直接的还是间接的。

为了继承而设计类的时候,Cloneable和Serializable接口出现了特殊的困难,因为clone和readObject方法在行为试试哪个非常类似与构造器,所以都不可以调用构造可覆盖的方法,无论直接还是间接的

如果决定在一个为了继承而设计的类中实现Serializable,并且该类有一个readResolev或者writeReplace方法,就必须使readResolve或者writeReplace成为受保护的方法。如果是私有的,子类将会不声不响的忽略掉这两个方法。

总结:对于那些并非为了安全的进行子类化而设计的和编写文档的类,要禁止子类化,有两种方法,一种是生命类为final的,还有一种是私有化类的构造方法(或者包级私有的),,并提供共有的静态方法类代替构造器。

第18条:接口优于抽象类

Java语言提供了两种机制,可以用来定义允许多个实现的类型:接口和抽象类。他们之间的区别为:
抽象类允许包含某些方法,但是接口则不允许,一个更重要的区别在于,为了实现抽象类定义的类型,类必须成为这个抽象类的子类,而任何一个类只要定了所需要的方法,遵守通用的约定,就允许实现一个接口。因为Java只允许单继承,所以抽象类作为类型定义受到了极大的限制

现有类可以很容易被更新,以实现新的接口

我们只需要添加必要的方法,就可以轻易的实现一个接口,然而,一般来说无法更新现有的类来扩展新的抽象类,除非让现有类的祖先成为新的抽象类的子类,但是这种伤害是非常大的。

接口是定义mixin(混合类型)的理想选择

mixin类型:类除了实现它的基本类型之外,还可以实现mixin类型,以表明某些可供选择的行为,例如实现了Comparable接口,就表明了该类的实例可以与其他的可比较对象进行排序。这样的接口称之为mixin接口,因为它允许任选的功能可以被混合到类型的主要功能中。而抽象类无法用于定义mixin。

接口允许我们构造非层次结构的类型框架。

类型层次对于组织某些事物是非常合适的,但是其他有些事务不能被整齐的组织成一个严格的层次结构。

//歌唱家
public interface Singer{

}
//作曲家
public Songwriter{

}

例如上面两个接口分别代表一个歌唱家,一个作曲家。在现实生活中,有些歌手本身也是作曲家,因为我们使用了接口来定义这两种类型,而不是抽象类,所以对单个类而言,同时实现两个接口是完全允许的。获取我们还可以定义第三个接口:

public interface SingerSongwriter extends Singer,Songwriter{

}

获取比并不需要这种灵活性,但是一旦你这样做了,接口可以成为救世主。

对于共性的抽象和功能的拓展一定要分清

在包装类模式中,使用接口使得安全的增强类的功能成为可能。

为接口提供骨架实现

虽然接口不允许包含方法的实现,但是,使用接口定义类型并不乏妨碍你为程序员提供实现上的帮助,对每个公共的接口都提供一个抽象的骨架实现(skeletal implementation)类,把接口和抽象类的有点结合起来,接口的作用仍然是定义类型,而骨架实现掌管了所有所有接口实现有关的工作

例如下面例子:

//java.util.Collections
public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
        return new SynchronizedCollection<>(c);
    }

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
......
}

在Collections中提供了把不安全的基本转变为线程安全的集合方法,充分的展示了骨架实现的强大功能。
骨架实现的美妙至于在于,它们为抽象类提供了实现上的帮助,又不强加抽象类被用作类型定义时所特有的严格限制。
骨架实现类可以很好的帮助接口的实现,实现了这个接口的类,可以把对于某一个接口方法的调用,转发到一个内部私有类的实例上,这个内部私有类扩展了骨架实现类,这种方法称为模拟多次继承,这项技术具有多重继承的绝大多数优点,同时又避免了响应的缺陷。

如何编写抽象类:
首先必须认真研究接口,并确定哪些方法是最基本的(primitive),其他方法则可以根据它们来实现,这些方法将成为接口中的抽象方法,然后骨架实现必须为借口中所有其他的方法提供具体的实现,

抽象类的演变比接口的演变要容易的多

如果在后续的发行版中中,希望在抽象类中增加新的方法始终可以增加具体的方法,然而接口是行不通的。虽然可以在接口的骨架实现中增加默认方法,但是不从骨架实现继承的接口实现还是会遭到破坏。因此:接口一旦被公开发行,并且被广泛的实现,要想再改变这个接口几乎是不可能的。

总结:接口通常是定义允许多个实现的类型的最佳途径,但是规则有一个例外,即当演变的容易性要灵活性和功能更加重要的时候。

你可能感兴趣的:(继承,Class,设计)