Java1.5中提供的兩種新類型
Item30: 用枚舉替代int型常量
枚舉:一系列常量類型的集合
沒有枚舉前大量定義的常量如下
// The int enum pattern - severely deficient!
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;
public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
首先調(diào)試不方便,我們只會(huì)打印出數(shù)字,然后回歸代碼
其次沒有命名空間做區(qū)別,命名累贅
使用枚舉后
public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
public enum Orange { NAVEL, TEMPLE, BLOOD }
再來看一個(gè)星球的例子
public enum Planet {
MERCURY(3.302e+23, 2.439e6),
VENUS (4.869e+24, 6.052e6),
EARTH (5.975e+24, 6.378e6),
MARS (6.419e+23, 3.393e6),
JUPITER(1.899e+27, 7.149e7),
SATURN (5.685e+26, 6.027e7),
URANUS (8.683e+25, 2.556e7),
NEPTUNE(1.024e+26, 2.477e7);
private final double mass; // In kilograms
private final double radius; // In meters
private final double surfaceGravity; // In m / s^2
// Universal gravitational constant in m^3 / kg s^2
private static final double G = 6.67300E-11;
// Constructor
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double mass() { return mass; }
public double radius() { return radius; }
public double surfaceGravity() { return surfaceGravity; }
public double surfaceWeight(double mass) {
return mass * surfaceGravity; // F = ma
}
}
再來看一個(gè)算數(shù)的例子
public enum Operation {
PLUS { double apply(double x, double y){return x + y;} },
MINUS { double apply(double x, double y){return x - y;} },
TIMES { double apply(double x, double y){return x * y;} },
DIVIDE { double apply(double x, double y){return x / y;} };
abstract double apply(double x, double y);
}
總結(jié):枚舉比int型常量更安全和具有可讀性
Item31: Use instance fields instead of ordinals(使用實(shí)例字段代替序號(hào))
所有的枚舉都有
ordinal 方法(從0開始計(jì)數(shù)),如果我們想用序號(hào)的int值(從1開始計(jì)數(shù)),不要直接修改ordinal 方法
//bad
public enum Ensemble {
SOLO, DUET, TRIO, QUARTET, QUINTET,
SEXTET, SEPTET, OCTET, NONET, DECTET;
public int numberOfMusicians() { return ordinal() + 1; }
}
雖然上述代碼能正常使用,但是卻會(huì)導(dǎo)致一場噩夢
如果我們改變了枚舉中的常量順序,之前的序號(hào)就會(huì)一團(tuán)糟
優(yōu)化代碼如下
public enum Ensemble {
SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
NONET(9), DECTET(10), TRIPLE_QUARTET(12);
private final int numberOfMusicians;
Ensemble(int size) {
this.numberOfMusicians = size;
}
public int numberOfMusicians() {
return numberOfMusicians;
}
}
Item32: Use EnumSet instead of bit fields(用EnumSet替代位字段)
如我們有一段代碼
// Bit field enumeration constants - OBSOLETE!
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) { ... }
}
//使用
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
用EnumSet優(yōu)化如下:
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set<Style> styles) { ... }
}
//使用
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
Item33: Use EnumMap instead of ordinal indexing(使用EnumMap代替順序索引)
Item34: 使用接口提升枚舉的擴(kuò)展性
code say everything
普通操作 加減乘除
// Emulated extensible enum using an interface
public interface Operation {
double apply(double x, double y);
}
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
現(xiàn)需要擴(kuò)展取余,異或等方法,不要再BasicOperation 中新加枚舉,而是實(shí)現(xiàn)公共接口Operation
// Emulated extension enum
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
REMAINDER("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
Item36: 始終使用override注解
// 你能找到這個(gè)bug嗎?
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> s = new HashSet<Bigram>();
for (int i = 0; i < 10; i++)
for (char ch = 'a'; ch <= 'z'; ch++)
s.add(new Bigram(ch, ch));
System.out.println(s.size());
}
}
理論上我們希望打印26,因?yàn)镠ashSet沒有重復(fù)元素,但是得到的值是260
因?yàn)闆]有override equals&hashCode方法,所以默認(rèn)是使用的"=="比較的地址,得到的都是不同的對象
修復(fù)如下
@Override public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
@Override public int hashCode() {
return 31 * first + second;
}
Item37: 使用標(biāo)記接口定義類型(待補(bǔ)充)
標(biāo)記接口:只為了標(biāo)記某一種類型
如 Serializable 接口,表明可將實(shí)例序列化
優(yōu)點(diǎn):
1.標(biāo)記接口定義了一種類型
2.可用于任何擴(kuò)展的子類標(biāo)記注解:
優(yōu)點(diǎn):
1.描述信息更豐富,可在使用后添加方法
2.位于FrameWork層,各個(gè)應(yīng)用使用同一套規(guī)則