在瞭解 J2SE 5.0 中新增的「列舉型態」(Enumerated Types)功能之前,您可以先瞭解一下在 J2SE 1.4 或之前版本中,是如何定義共用常數的,如此在接觸列舉型態時,您就可以感覺它所帶來的更多好處。
有時候您會需要定義一些常數供程式使用,您可以使用介面或類別來定義,例如您可以使用介面來定義操作時所需的共用常數,範例 11.1 是個簡單示範。
public interface ActionConstants {
public static final int TURN_LEFT = 1;
public static final int TURN_RIGHT = 2;
public static final int SHOT = 3;
}
共用的常數通常是可以直接取用並且不可被修改的,所以您在宣告時加上 "static" 與 "final",如此您可以在程式中直接使用像是 ActionConstants.TURN_LEFT 的名稱來取用常數值,例如:
public void someMethod() {
....
doAction(ActionConstants.TURN_RIGHT);
....
}
public void doAction(int action) {
switch(action) {
case ActionConstants.TURN_LEFT:
System.out.println("向左轉");
break;
case ActionConstants.TURN_RIGHT:
System.out.println("向右轉");
break;
case ActionConstants.SHOOT:
System.out.println("射擊");
break;
}
}
如果使用類別來宣告的話,方法也是類似,例如:
public class CommandTool {
public static final String ADMIN = "onlyfun.caterpillar.admin";
public static final String DEVELOPER = "onlyfun.caterpillar.developer";
public void someMethod() {
// ....
}
}
如果常數只是在類別內部使用的話,就宣告其為 "private" 或是 "protected" 就可以了,宣告為類別外可取用的常數,通常是與類別功能相依的常數,例如使用 CommandTool 時若會使用到與 CommandTool 功能相依的常數的話,將這些常數直接宣告在 CommandTool 類別上取用時就很方便,而使用介面所宣告的常數,則通常是整個程式或某個模組中都會共用到的常數。
對於簡單的常數設置,上面的作法已經足夠了,在 J2SE 5.0 中則新增了「列舉型態」(Enumerated Types),使用列舉型態,除了簡單的常數設定功能之外,您還可以獲得像編譯時期型態檢查等更多的好處。
良葛格的話匣子 宣告常數時,通常使用大寫字母,並可以底線來區隔每個單字以利識別,例如像TURN_LEFT這樣的名稱。
您已經知道可以在類別或介面中宣告常數來統一管理常數,這只是讓您存取與管理常數方便而已,來看看這個例子:
public void someMethod() {
....
doAction(ActionConstants.TURN_RIGHT);
....
}
public void doAction(int action) {
switch(action) {
case ActionConstants.TURN_LEFT:
System.out.println("向左轉");
break;
..
}
}
這種作法本身沒錯,只不過 doAction() 方法接受的是int型態的常數,您沒有能力阻止程式設計人員對它輸入 ActionConstants 規定外的其它常數,也沒有檢查 "switch" 中列舉的值是不是正確的值,因為參數 action 就只是 int 型態而已,當然您可以自行設計一些檢查動作,這需要一些額外的工作,如果您使用 J2SE 5.0 中新增的「列舉型態」(Enumerated Types),就可以無需花額外的功夫就輕易的解決這些問題。
在 J2SE 5.0 中要定義列舉型態是使用 "enum" 關鍵字,以下先來看看列舉型態的應用,舉個實際的例子,範例 11.3 是定義了 Action 列舉型態。
public enum Action {
TURN_LEFT,
TURN_RIGHT,
SHOOT
}
不用懷疑,在 Action.java 中撰寫範例 11.3 的內容然後編譯它,雖然語法上不像是在定義類別,但列舉型態骨子裏就是一個類別,所以您編譯完成後,會產生一個 Action.class 檔案。
來看下面範例 11.4 瞭解如何使用定義好的列舉型態。
public class EnumDemo {
public static void main(String[] args) {
doAction(Action.TURN_RIGHT);
}
public static void doAction(Action action) {
switch(action) {
case TURN_LEFT:
System.out.println("向左轉");
break;
case TURN_RIGHT:
System.out.println("向右轉");
break;
case SHOOT:
System.out.println("射擊");
break;
}
}
}
執行結果:
向右轉
除了讓您少打一些字之外,這個範例好像沒有什麼特別的,但注意到 doAction() 參數列的型態是 Action,如果您對 doAction() 方法輸入其它型態的引數,編譯器會回報錯誤,因為 doAction() 所接受的引數必須是 Action 列舉型態。 使用列舉型態還可以作到更進一步的檢驗,如果您在 "switch" 中加入了不屬於 Action 中列舉的值,編譯器也會回報錯誤,例如:
...
public static void doAction(Action action) {
switch(action) {
case TURN_LEFT:
System.out.println("向左轉");
break;
case TURN_RIGHT:
System.out.println("向右轉");
break;
case SHOOT:
System.out.println("射擊");
break;
case STOP: // Action中沒有列舉這個值
System.out.println("停止");
break;
}
} ...
在編譯時編譯器會替您作檢查,若檢查出不屬於 Action 中的列舉值,會顯示以下的錯誤:
unqualified enumeration constant name required
case STOP:
^
您可以在一個獨立的檔案中宣告列舉值,或是在某個類別中宣告列舉成員,例如範例 11.5 將 Action 列舉型態宣告於 EnumDemo2 類別中。
public class EnumDemo2 {
private enum InnerAction {TURN_LEFT, TURN_RIGHT, SHOOT};
public static void main(String[] args) {
doAction(InnerAction.TURN_RIGHT);
}
public static void doAction(InnerAction action) {
switch(action) {
case TURN_LEFT:
System.out.println("向左轉");
break;
case TURN_RIGHT:
System.out.println("向右轉");
break;
case SHOOT:
System.out.println("射擊");
break;
}
}
}
執行結果:
向右轉
由於列舉型態本質上還是個類別,所以範例11.5的列舉宣告方式有些像在宣告「內部類別」(Inner class),在您編譯完 EnumDemo2.java 檔案後,除了 EnumDemo2.class 之外,您會有一些額外的 .class 檔案產生,在這個例子中就是 EnumDemo2$InnerAction.class 與 EnumDemo2$1.class,看到這兩個檔案,您就應該瞭解實際上編譯器產生了「成員內部類別」以及「匿名內部類別」(第 9 章說明過內部類別)。