Effective Java 3rd 條目22 僅僅定義型別時使用介面
當一個類實現一個介面時,這個介面起一個型別的作用,可以被用來引用這個類的例項。一個類實現了一個介面,應該因此說明一些事情:使用這個類的例項,一個類可以做什麼。為任何其他目的而定義一個介面,都是不恰當的。
不符合這個檢測的一種介面是所謂的常量介面(constant interface)。這樣的介面不包含任何方法;它僅僅包含靜態final域,每個域匯出一個常量。使用這些常量的類實現這個介面,避免了用類名限定常量名的必要。如下是個例子:
// 常量介面反模式 - 不要使用!
public interface PhysicalConstants {
// 阿伏伽德羅常數 (1/mol)
static final double AVOGADROS_NUMBER = 6.022_140_857e23;
// 玻爾茲曼常數 (J/K)
static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
// 電荷質量 (kg)
static final double ELECTRON_MASS = 9.109_383_56e-31;
}
常量介面模式是介面的糟糕使用。一個類內部使用一些常數是實現細節。實現一個常數介面造成這個實現細節洩漏到這個類的匯出API中。類實現了一個常量介面,對於這個類的使用者來說是不重要的。事實上,它可能令他們困惑。更為糟糕的是,它代表著一種承諾:如果在以後的釋出中這個類改變了以致於它不再需要使用這些常量,那麼它仍舊必須實現這個介面來保證兩者的相容性。如果一個非final類實現了一個常量介面,那麼他的所有子類將使得它們的名稱空間被這個介面中的常量汙染。
Java平臺庫中有許多常量介面,比如java.io.ObjectStreamConstants。這些介面應該看成異常情況,而不應該模仿。
如果你想匯出常數,有許多合理的選擇。如果常數是和已存類或者介面聯絡緊密,那麼你應該新增它們到這個類或者介面。比如,所有數值原始裝箱類,比如Integer和Double,匯出MIN_VALUE和MAX_VALUE常量。如果常量最好看成是列舉型別的成員,那麼你應該匯出它們為enum型別(enum type)(條目34)。否則,你應該使用一個不可例項化的效用類(utility class)(條目4)匯出常量。下面是早先表述的PhysicalConstants例子的效用類版本:
// 常量效用類
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() { }// 防止例項化
、
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;
}
順便提一下,注意到數值字面量中的下劃線字元(_)的使用。下劃線自從Java7以來就是合法的,它沒有影響到數值字面量的值,但是如果謹慎使用,可以使得它們更容易閱讀。如果浮點數包含了五個或者更多的連續數字,不管是否是確定的,考慮新增下劃線到數值字面量。對於至少十個字面值,不管是整數的還是浮點數,你應該使用下劃線劃分字面值到三個數的組,它代表正的或者負的以一千的冪。
通常,效用類需要客戶端使用類名限定常數名字,例如,PhysicalConstants.AVOGADROS_NUMBER。如果你大量使用效用類匯出的常數,那麼你應該通過使用靜態匯入(static import)功能避免使用類名限定常數:
// 通過靜態匯入避免限定常數
import static com.effectivejava.science.PhysicalConstants.*;
public class Test {
double atoms(double mols) {
return AVOGADROS_NUMBER * mols;
}
...
// PhysicalConstants更多的使用證明靜態匯入是正當的
}
總之,介面應該只在定義型別時使用。它們不應該僅僅匯出常量時使用。