接口只用来定义类型

如果一个类实现了某个接口,那么这个接口类型可以成为指向该类对象的引用。因此类与接口之间的"实现"关系,应该能使用户通过接口了解对象的行为。除了这个场景以外,定义接口都是不合适的。违背上述原则的一个反例是常量接口,这样的接口不定义任何方法,仅含有静态final属性,每一个属性都被开放为常量。用户类通过实现这些接口来避免使用常量全名。比如下面的例子:

// 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常量。如果常量可被视作枚举类型的成员,那么他们应该被定义为枚举类型(enum type)。否则,常量应该通过不可实例化的工具类(item 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。如果使用频繁,可以通过静态导入(static import)来简化代码:

// 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
}

总之,接口必须用来定义类型,而不能用来定义常量。