接口和抽象类
Java 有两种机制来定义允许多个实现的类型:接口和抽象类。由于在 Java 8 中引入了接口的默认方法(default methods ),因此这两种机制都允许为某些实例方法提供实现。
一个接口通常是定义允许多个实现的类型的最佳方式。如果你导出一个重要的接口,应该强烈考虑提供一个骨架的实现类。在可能的情况下,应该通过接口上的默认方法提供骨架实现,以便接口的所有实现者都可以使用它。也就是说,对接口的限制通常要求骨架实现类采用抽象类的形式。
骨架实现类的优点在于:
小结
在 Java 8 之前,不可能在不破坏现有实现的情况下为接口添加方法。如果向接口添加了一个新方法,现有的实现通常会缺少该方法,从而导致编译时错误。在 Java 8 中,添加了默认方法(default method)构造,目的是允许将方法添加到现有的接口。但是增加新的方法到现有的接口是充满风险的。
默认方法的声明包含一个默认实现,该方法允许实现接口的类直接使用,而不必实现默认方法。虽然在 Java 中添加默认方法可以将方法添加到现有接口,但不能保证这些方法可以在所有已有的实现中使用。默认的方法被「注入(injected)」到现有的实现中,没有经过实现类的知道或同意。在 Java 8 之前,这些实现是用默认的接口编写的,它们的接口永远不会获得任何新的方法。
在默认方法的情况下,接口的现有实现类可以在没有错误或警告的情况下编译,但在运行时会失败。 虽然不是非常普遍,但这个问题也不是一个孤立的事件。在 Java 8 中添加到集合接口的一些方法已知是易受影响的,并且已知一些现有的实现会受到影响。
应该避免使用默认方法向现有的接口添加新的方法,除非这个需要是关键的,在这种情况下,你应该仔细考虑,以确定现有的接口实现是否会被默认的方法实现所破坏。然而,默认方法对于在创建接口时提供标准的方法实现非常有用,以减轻实现接口的任务。
因此,在发布之前测试每个新接口是非常重要的。多个程序员应该以不同的方式实现每个接口。至少,你应该准备三种不同的实现。编写多个使用每个新接口的实例来执行各种任务的客户端程序同样重要。这将大大确保每个接口都能满足其所有的预期用途。这些步骤将允许你在发布之前发现接口中的缺陷,但仍然可以轻松地修正它们。虽然在接口被发布后可能会修正一些存在的缺陷,但不要太指望这一点。
小结
当类实现接口时,该接口作为一种类型(type),可以用来引用类的实例。因此,一个类实现了一个接口,因此表明客户端可以如何处理类的实例。为其他目的定义接口是不合适的。
如果你想导出常量,有几个合理的选择方案。如果常量与现有的类或接口紧密相关,则应将其添加到该类或接口中。
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() { } // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.022_140_857e23;
public static final double BOLTZMANN_CONST = 1.380_648_52e-23;
public static final double ELECTRON_MASS = 9.109_383_56e-31;
}
从 Java 7 开始,合法的下划线对数字字面量的值没有影响,但是如果使用得当的话可以使它们更容易阅读。无论是固定的浮点数,如果他们包含五个或更多的连续数字,考虑将下划线添加到数字字面量中。对于底数为 10 的数字,无论是整型还是浮点型的,都应该用下划线将数字分成三个数字组,表示一千的正负幂。
小结