Effective java 讀書筆記( 四 )

30.用enum代替int常量

1.在編程語言還沒有引入枚舉類型之前,表示枚舉類型的常用模式是聲明一組具名的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;
	
這種方法稱作“int枚舉類型(Int enum pattern),存在着諸多不足。下面着重介紹一下這些不足,從而有助於理解枚舉類型的優勢和不足,從而方便在實際開發中根據實際需求進行取捨。
2.int枚舉類型在類型安全性和使用方便性方面是”蹩腳的“,如果你將apple傳到想要orange的方法中,編譯器不會出現警告,還會用==操作符將apple與orange進行對比,甚至更糟。
3.注意每個apple常量的名稱都以APPLE_作爲前綴,每個orange常量則都以ORANGE_作爲前綴,這是因爲java沒有爲int枚舉組提供命名空間。當兩個int枚舉組具有相同的命名常量時,前綴可以防止名稱發生衝突,但是瞭解了枚舉類型後便會發現那不是最佳實踐
4.採用int枚舉類型模式的程序是十分脆弱的,因爲int枚舉是編譯時常量,被編譯到使用它們的客戶端中。如果與枚舉常量關聯的int發生了變化,客戶端就必須重新編譯。如果沒有重新編譯,程序還是可以運行,但是它們的行爲就是不確定的
5.將int枚舉常量翻譯成可打印的字符串,並沒有很便利的方法。如果將這種常量打印出來,或者從調試器中將它顯示出來,你所見到的的就是一個數字,這沒有太大的意義。要遍歷一個組中的所有int枚舉常量,甚至獲得int枚舉組的大小,這些都沒有很可靠的方法
6.int枚舉模式的變體是String枚舉模式,雖然這種模式提供了可打印的字符串,但是它會導致性能問題,因爲它依賴於字符串的比較操作,更糟的是,它會導致初級用戶把字符串常量硬編碼到客戶端代碼中,而不是使用適當的域名。如果這樣的硬編碼字符串常量中包含有書寫錯誤,那麼,這樣的錯誤在編譯時不會被檢測到,但是在運行的時候卻會報錯
7.由於存在上述諸多問題,java在1.5版本中引入了枚舉類型,示例代碼如下:
// Enum type with data and behavior
	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
		}
	}

8.枚舉類型提供了編譯時的類型安全,也就是說能保證該傳入apple類型的參數不會誤傳入orange
9.包含同名常量的多個枚舉類型可以在一個系統中和平共處,因爲每個類型都有自己的命名空間
10.你可以增加或者重新排列枚舉類型中的常量,而無需重新編譯它的客戶端代碼,因爲導出常量的域在枚舉類型和它的客戶端之間提供了一個隔離層:常量值並沒有被編譯到客戶端代碼中,而是在int枚舉模式之中
11.可以通過調用toString方法,將枚舉轉換成可打印的字符串
12.除了完善了int枚舉類型的不足之外,枚舉類型還允許添加任意的方法和域,並實現任意的接口示,例代碼:
// Enum type with data and behavior
	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
		}

	}

	public static void main(String[] args) {
		double earthWeight = Double.parseDouble(args[0]);
		double mass = earthWeight / Planet.EARTH.surfaceGravity();
		for (Planet p : Planet.values())
			System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass));
	}

31.用實例域代替序數

32.用EnumSet代替位域

33.用EnumMap代替序數索引

34.用接口模擬可伸縮的枚舉

35.註解優先於命名模式

36.堅持使用override註解

37.用標記接口定義類型




END
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章