来自 JOshua Bloch 的《Effective java》一书
第 19 条:只使用接口来定义类型
当一个类实现一个接口时,该接口充当一种可用于引用该类实例的类型。因此,一个类实现了一个接口应该说明客户端可以对类的实例做什么。为任何其他目的定义接口是不合适的。
一种未通过此测试的接口是所谓的常量接口。这样的接口不包含任何方法;它仅由静态最终字段组成,每个字段都导出一个常量。使用这些常量的类实现接口以避免使用类名来限定常量名。这是一个例子:
// Constant interface antipattern - do not use!
public interface PhysicalConstants {
// Avogadro's number (1/mol)
static final double AVOGADROS_NUMBER = 6.02214199e23;
// Boltzmann constant (J/K)
static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
// Mass of the electron (kg)
static final double ELECTRON_MASS = 9.10938188e-31;
}
常量接口模式是接口使用不当。一个类在内部使用一些常量是一个实现细节。实现一个常量接口会导致这个实现细节泄漏到类的导出 API 中。类实现一个常量接口对类的用户来说无关紧要。事实上,它甚至可能使他们感到困惑。更糟糕的是,它代表了一种承诺:如果在未来的版本中修改了类以使其不再需要使用常量,它仍然必须实现接口以确保二进制兼容性。如果一个非最终类实现了一个常量接口,那么它的所有子类的命名空间都会被接口中的常量污染。
Java 平台库中有几个常量接口,例如 java.io.ObjectStreamConstants。这些接口应被视为异常,不应被模仿。
如果要导出常量,有几种合理的选择。如果常量与现有的类或接口紧密相关,则应将它们添加到类或接口中。例如,所有装箱的数值基元类(如 Integer 和 Double)都导出 MIN_VALUE 和 MAX_VALUE 常量。如果最好将常量视为枚举类型的成员,则应该使用枚举类型导出它们(第 30 条)。否则,您应该使用不可实例化的实用程序类(第 4 项)导出常量。这是上面 PhysicalConstants 示例的实用程序类版本:
// Constant utility class
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() {
} // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}
通常,实用程序类要求客户端使用类名来限定常量名,例如 PhysicalConstants.AVOGADROS_NUMBER。如果您大量使用实用程序类导出的常量,则可以通过使用 1.5 版中引入的静态导入工具来避免使用类名限定常量:
// Use of static import to avoid qualifying constants
import static com.effectivejava.science.PhysicalConstants.*;
public class Test {
double atoms(double mols) {
return AVOGADROS_NUMBER * mols;
}
...
// Many more uses of PhysicalConstants justify static import
}
总之,接口应该只用于定义类型。它们不应该用于导出常量。