前言
程式中經常會使用到一些常數,如果有些常數是共用的,在 Java 中可以定義一個類別或介面來統一管理常數,而其它物件從這些類別或介面上取用常數,如果需要修改常數則可以從這些類別或介面上直接修改,而不用更動到程式的其它部份,這種使用常數的方式在 J2SE 1.4 或之前的版本相當常見。
J2SE 5.0 中新增了「列舉型態」(Enumerated Types),您可以使用這個功能取代之前 J2SE 1.4 或之前版本定義常數的方式,除了常數設置的功能之外,列舉型態還給了您許多編譯時期的檢查功能,但別想的太複雜,列舉型態本質上還是以類別的方式存在,因而它提供了這麼多額外的功能並不奇怪。
一、列舉常數值
在日常生活中,項目固定、值固定,請用列舉。列舉必須寫在類別內部,屬於內部類別。
語法:
編譯器會自動加上存取修飾值 public static final
範例:星期
class TestEnum1{
enum Week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
public static void main(String[] arg){
System.out.println(Week.Sunday);
System.out.println(Week.Monday);
System.out.println(Week.Tuesday);
System.out.println(Week.Wednesday);
System.out.println(Week.Thursday);
System.out.println(Week.Friday);
System.out.println(Week.Saturday);
}
}

使用 DJ Java Decompiler 反組譯後,發現 javac 加了很多程式碼。

二、列舉的功能應用
2.1 enum 的相關事項
◉ 列舉的內容值都經過 public final static 的修飾。
◉ 列舉內容一經初始給定後就無法更改。
◉ 列舉值可以用「==」或「equals()」來比較。
◉ 列舉內容值在內部排序與宣告時順序一致。
◉ 列舉內實作了 java.lang.Comparable 介面。
◉ 列舉內容值的資料型別就是該列舉的類別。
2.2 enum 搭配 for 迴圈使用
取出列舉內所有的常數值,可以使用 values() 方法取出一個陣列,回傳 TestEnum2$Week[] 類別。
xxxxxxxxxx
class TestEnum2{
enum Week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
public static void main(String[] arg){
//System.out.println(Week.Sunday); //會呼叫 Week.Sunday.toString()
System.out.println(Week.valueOf("Sunday"));
System.out.println(Week.Monday);
System.out.println(Week.Tuesday);
System.out.println(Week.Wednesday);
System.out.println(Week.Thursday);
System.out.println(Week.Friday);
System.out.println(Week.Saturday);
System.out.println();
//列舉搭配加強版 for 迴圈使用
Week[] wk = Week.values();
for(Week tmp : wk){
System.out.println(tmp);
}
System.out.println();
//列舉搭配 for 迴圈使用
for(int i=0; i<wk.length; i++){
System.out.println(wk[i]);
}
System.out.println();
//不用先產生一個陣列變數,即可以使用加強版for迴圈
for(Week tmp:Week.values()){
System.out.println(tmp);
}
}
}

2.3 enum 的位置索引值
對於每一個列舉成員,您可以使用 ordinal() 方法,依列舉順序得到位置索引,預設以 0 開始。
xxxxxxxxxx
class TestEnum3{
enum Week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
public static void main(String[] arg){
for(Week tmp : Week.values()){
System.out.printf(" %d %s%n", tmp.ordinal(), tmp);
}
}
}

2.4 enum 搭配 switch 使用
此時需要從列舉取出位置索引,如此就可以搭配 switch 使用。
我們可以使用 valueOf() 方法,Java 會自動使用列舉常數值判斷。
如果您在"switch"中加入了不屬於 Week 中列舉的值,編譯器會回報錯誤。
xxxxxxxxxx
import java.util.Scanner;
class TestEnum4 {
enum Week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
public static void main(String[] arg){
String str = "";
Scanner sc = new Scanner(System.in);
System.out.println("請輸入星期的英文");
str=sc.next();
//如果 str="Wednesday", 則 Week.valueOf(str) 會呼叫 Week.Wednesday.toString()。
System.out.print(Week.valueOf(str) + "折扣是:");
switch(Week.valueOf(str)){
case Sunday:
System.out.println("3折");
break;
case Monday:
System.out.println("8折");
break;
case Tuesday:
System.out.println("7折");
break;
case Wednesday:
System.out.println("2.5折");
break;
case Thursday:
System.out.println("6折");
break;
case Friday:
System.out.println("2折");
break;
case Saturday:
System.out.println("3.5折");
break;
}
}
}

三、列舉是一個類別
3.1 enum 直接使用自己的建構子方法
列舉型態既然是類別,那麼您可以為它加上建構方法(Constructor)嗎?答案是可以的,但是不得為公開的(public)建構方法,這是為了避免粗心的程式設計人員直接對列舉型態實例化,一個不公開的建構方法可以作什麼?
在列舉 Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday 成員時,您可以一併指定文字描述,這個描述會在建構列舉物件時使用,下個範例中您將之設定給私用成員 msg,在使用 format() 時將它串接返回。

xxxxxxxxxx
class TestEnum5 {
enum Week{
//列舉常數值後面加上(),可以馬上呼叫Week的建構子。
//()內有幾筆資料,建構子就必須寫上相對應的參數。
Sunday("星期天"),Monday("星期一"),Tuesday("星期二"),Wednesday("星期三"),
Thursday("星期四"),Friday("星期五"),Saturday("星期六");
private String msg;
//建構子與上方列舉常數值()內的數量必須一致。
private Week(String m){
msg = m;
}
public String format(String str){
return str + msg;
}
}
public static void main(String[] arg){
System.out.println(Week.Sunday.format("今天是"));
System.out.println(Week.Monday.format("明天是"));
System.out.println(Week.Tuesday.format("後天是"));
}
}

建構子接收兩個參數的範例
xxxxxxxxxx
class TestEnum6 {
enum Week{
Sunday("星期天", 3), Monday("星期一", 8), Tuesday("星期二", 7), Wednesday("星期三", 2.5),
Thursday("星期四", 6), Friday("星期五", 2), Saturday("星期六", 3.5);
private String msg;
private double discount;
private Week(String m, double d){
msg = m;
discount = d;
}
public String format(){
return msg + "的折扣是" + discount + "折";
}
}
public static void main(String[] arg){
System.out.println(Week.Sunday.format());
System.out.println(Week.Monday.format());
System.out.println(Week.Tuesday.format());
}
}

3.2 Value-Specific Class Bodies
實作時像是在使用「匿名內部類別」(Anonymous inner class),它讓您可以為每個列舉值定義各自的類本體與方法(Method)實作。
實作覆寫(Overriding) toString() 方法。
xxxxxxxxxx
class TestEnum7 {
enum Week{
//覆蓋 toString() 的專屬寫法,不能覆蓋其它父方法。
//在列舉常數值後使用{},類似 static{} 的靜態區塊一樣,直接載入覆蓋 toString()。
Sunday{
public String toString(){
return "星期日折扣是 3折";
}
}, //與另一個列舉常數值隔開,使用逗號。
Monday{
public String toString(){
return "星期一折扣是 8折";
}
},
Tuesday{
public String toString(){
return "星期二折扣是 7折";
}
},
Wendesday{
public String toString(){
return "星期三折扣是 2.5折";
}
},
Thursday{
public String toString(){
return "星期四折扣是 6折";
}
},
Friday{
public String toString(){
return "星期五折扣是 2折";
}
},
Saturday{
public String toString(){
return "星期一折扣是 3.5折";
}
}; //沒有列舉常數值需要撰寫,使用分號結束。
}
public static void main(String[] arg){
System.out.println(Week.Sunday);//如果沒有覆蓋toString(),則會回傳原來的Sunday字串。
System.out.println(Week.Monday);
System.out.println(Week.Tuesday);
}
}

3.3 使用 compareTo() 方法比較兩個列舉物件
compareTo() 如果傳回正值,表示設定為引數的列舉物件(inputWeek)其順序在比較的列舉物件(week)之前,負值表示在之後,而 0 則表示兩個互比列舉值的位置是相同的,執行結果如下:
xxxxxxxxxx
class TestEnum8{
enum Week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
public static void main(String[] arg){
Week inputWeek = Week.valueOf(arg[0]);
for(Week week : Week.values()){
System.out.println(week.compareTo(inputWeek));
}
}
}

四、總結
使用 enum 來取代常數列舉,列舉型態實際上就是在定義一個類別,兩個公開靜態方法 values() 與 valueOf(),一個私有物件參考變數 $VALUES[],其屬性變數都是 static final。
列舉常數值依照宣告的順序給予位置索引值,可以使用 ordinal() 方法拿取位置索引值。可以使用「==」、「equals()」和「compareTo()」來比較兩個列舉物件。
建構子只能是 private 與 default,其參數個數與列舉常數後的括號參數數量一致。只能覆寫 toString(),其餘方法皆不可以覆寫。
沒有留言:
張貼留言