<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    302班

    java突擊隊
    posts - 151, comments - 74, trackbacks - 0, articles - 14
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    枚舉類型及其應用(轉載)

    Posted on 2007-05-22 15:04 停留的風 閱讀(869) 評論(0)  編輯  收藏 所屬分類: 美文轉載

    Brett McLaughlin (brett@newInstance.com), 作者/編輯, O'Reilly Media, Inc.
    2004 年 11 月 09 日

    Tiger 中的一個重要新特性是枚舉構造,它是一種新的類型,允許用常量來表示特定的數據片斷,而且全部都以類型安全的形式來表示。Tiger 專家、developerWorks 的多產作者 Brett McLaughlin 將解釋枚舉的定義,介紹如何在應用程序中運用枚舉,以及它為什么能夠讓您拋棄所有舊的 public static final 代碼。

    您已經知道,Java 代碼的兩個基本的構造塊是 接口。現在 Tiger 又引入了 枚舉,一般簡稱它為 enum。這個新類型允許您表示特定的數據點,這些數據點只接受分配時預先定義的值集合。

    當然,熟練的程序員可以用靜態常量實現這項功能,如清單 1 所示:



    清單 1. public static final 的常量
    public class OldGrade {
                public static final int A = 1;
                public static final int B = 2;
                public static final int C = 3;
                public static final int D = 4;
                public static final int F = 5;
                public static final int INCOMPLETE = 6;
                }
                

    說明:我要感謝 O'Reilly 媒體公司,該公司允許在本文中使用我撰寫的 Java 1.5 Tiger: A Developer's Notebook一書中“枚舉”這一章中的代碼示例(請參閱 參考資料)。

    然后您就可以讓類接受像 OldGrade.B 這樣的常量,但是在這樣做的時候,請記住這類常量是 Java 中 int 類型的常量,這意味著該方法可以接受任何 int 類型的值,即使它和 OldGrade 中定義的所有級別都不對應。因此,您需要檢測上界和下界,在出現無效值的時候,可能還要包含一個 IllegalArgumentException 。而且,如果后來又添加另外一個級別(例如 OldGrade.WITHDREW_PASSING ),那么必須改變所有代碼中的上界,才能接受這個新值。

    換句話說,在使用這類帶有整型常量的類時,該解決方案也許可行,但并不是非常有效。幸運的是,枚舉提供了更好的方法。





    定義枚舉

    清單 2 使用了一個可以提供與清單 1 相似的功能的枚舉:



    清單 2. 簡單的枚舉類型
    package com.oreilly.tiger.ch03;
                public enum Grade {
                A, B, C, D, F, INCOMPLETE
                };
                

    在這里,我使用了新的關鍵字 enum ,為 enum 提供了一個名稱,并指定了允許的值。然后, Grade 就變成了一個 枚舉類型,您可以按清單 3 所示的方法使用它:



    清單 3. 使用枚舉類型
    package com.oreilly.tiger.ch03;
                public class Student {
                private String firstName;
                private String lastName;
                private Grade grade;
                public Student(String firstName, String lastName) {
                this.firstName = firstName;
                this.lastName = lastName;
                }
                public void setFirstName(String firstName) {
                this.firstName = firstName;
                }
                public String getFirstName() {
                return firstName;
                }
                public void setLastName(String lastName) {
                this.lastName = lastName;
                }
                public String getLastName() {
                return lastName;
                }
                public String getFullName() {
                return new StringBuffer(firstName)
                .append(" ")
                .append(lastName)
                .toString();
                }
                public void assignGrade(Grade grade) {
                this.grade = grade;
                }
                public Grade getGrade() {
                return grade;
                }
                }
                

    用以前定義過的類型建立一個新的枚舉( grade )之后,您就可以像使用其他成員變量一樣使用它了。當然,枚舉只能分配枚舉值中的一個(例如, A 、 CINCOMPLETE )。而且,在 assignGrade() 中是沒有進行錯誤檢測的代碼,也沒有考慮邊界情況,請注意這是如何做到。







    使用枚舉值

    迄今為止,您所看到的示例都相當簡單,但是枚舉類型提供的東西遠不止這些。您可以逐個遍歷枚舉值,也可以在 switch 語句中使用枚舉值,枚舉是非常有價值的。

    遍歷枚舉值

    下面我們用一個示例顯示如何遍歷枚舉類型的值。清單 4 所示的這項技術,適用于調試、快速打印任務以及把枚舉加載到集合(我很快將談到)中的工具:



    清單 4. 遍歷枚舉值
    public void listGradeValues(PrintStream out) throws IOException {
                for (Grade g : Grade.values()) {
                out.println("Allowed value: '" + g + "'");
                }
                }
                

    運行這段代碼,將得到清單 5 所示的輸出:



    清單 5. 迭代操作的輸出
    Allowed Value: 'A'
                Allowed Value: 'B'
                Allowed Value: 'C'
                Allowed Value: 'D'
                Allowed Value: 'F'
                Allowed Value: 'INCOMPLETE'
                

    這里有許多東西。首先,我使用了 Tiger 的新的 for/in 循環(也叫作 foreach增強的 for )。另外,您可以看到 values() 方法返回了一個由獨立的 Grade 實例構成的數組,每個數組都有一個枚舉類型的值。換句話說, values() 的返回值是 Grade[] 。

    在枚舉間切換

    能夠在枚舉的值之間移動很好,但是更重要的是根據枚舉的值進行決策。您當然可以寫一堆 if (grade.equals(Grade.A)) 類型的語句,但那是在浪費時間。Tiger 能夠很方便地把枚舉支持添加到過去的好東西 switch 語句上,所以它很容易使用,而且適合您已知的內容。清單 6 向將展示如何解決這個難題:



    清單 6. 在枚舉之間切換
    public void testSwitchStatement(PrintStream out) throws IOException {
                StringBuffer outputText = new StringBuffer(student1.getFullName());
                switch (student1.getGrade()) {
                case A:
                outputText.append(" excelled with a grade of A");
                break;
                case B: // fall through to C
                case C:
                outputText.append(" passed with a grade of ")
                .append(student1.getGrade().toString());
                break;
                case D: // fall through to F
                case F:
                outputText.append(" failed with a grade of ")
                .append(student1.getGrade().toString());
                break;
                case INCOMPLETE:
                outputText.append(" did not complete the class.");
                break;
                }
                out.println(outputText.toString());
                }
                

    在這里,枚舉值被傳遞到 switch 語句中(請記住, getGrade() 是作為 Grade 的實例返回的),而每個 case 子句將處理一個特定的值。該值在提供時沒有枚舉前綴,這意味著不用將代碼寫成 case Grade.A ,只需將其寫成 case A 即可。如果您不這么做,編譯器不會接受有前綴的值。

    現在,您應該已經了解使用 switch 語句時的基本語法,但是還有一些事情您需要知道。

    在使用 switch 之前進行計劃

    正如您所期待的,在使用枚舉和 switch 時,您可以使用 default 語句。清單 7 顯示了這個用法:



    清單 7. 添加一個 default 塊
    public void testSwitchStatement(PrintStream out) throws IOException {
                StringBuffer outputText = new StringBuffer(student1.getFullName());
                switch (student1.getGrade()) {
                case A:
                outputText.append(" excelled with a grade of A");
                break;
                case B: // fall through to C
                case C:
                outputText.append(" passed with a grade of ")
                .append(student1.getGrade().toString());
                break;
                case D: // fall through to F
                case F:
                outputText.append(" failed with a grade of ")
                .append(student1.getGrade().toString());
                break;
                case INCOMPLETE:
                outputText.append(" did not complete the class.");
                break;
                default:
                outputText.append(" has a grade of ")
                .append(student1.getGrade().toString());
                break;
                }
                out.println(outputText.toString());
                }
                

    研究以上代碼可以看出,任何沒有被 case 語句處理的枚舉值都會被 default 語句處理。這項技術您應當 堅持采用。原因是:假設 Grade 枚舉被您的小組中其他程序員修改(而且他忘記告訴您這件事)成清單 8 所示的版本:



    清單 8. 給 Grade 枚舉添加一個值
    package com.oreilly.tiger.ch03;
                public enum Grade {
                A, B, C, D, F, INCOMPLETE
                ,
                WITHDREW_PASSING, WITHDREW_FAILING
                };
                

    現在,如果使用清單 6 的代碼所示的新版 Grade ,那么這兩個新值會被忽略。更糟的是,您甚至看不到錯誤!在這種情況下,存在某種能夠通用的 default 語句是非常重要的。清單 7 無法很好地處理這些值,但是它會提示您還有其他值,您需要處理這些值。一旦完成處理,您就會有一個繼續運行的應用程序,而且它不會忽略這些值,甚至還會指導您下一步的動作。所以這是一個良好的編碼習慣。







    枚舉和集合

    您所熟悉的使用 public static final 方法進行編碼的那些東西,可能已經轉而采用枚舉的值作為映射的鍵。如果您不知道其中的含義,請參見清單 9,它是一個公共錯誤信息的示例,在使用 Ant 的 build 文件時,可能會彈出這樣的消息,如下所示:



    清單 9. Ant 狀態碼
    package com.oreilly.tiger.ch03;
                public enum AntStatus {
                INITIALIZING,
                COMPILING,
                COPYING,
                JARRING,
                ZIPPING,
                DONE,
                ERROR
                }
                

    為每個狀態碼分配一些人們能讀懂的錯誤信息,從而允許人們在 Ant 提供某個代碼時查找合適的錯誤信息,將這些信息顯示在控制臺上。這是 映射(Map) 的一個絕好用例,在這里,每個 映射(Map) 的鍵都是一個枚舉值,而每個值都是鍵的錯誤信息。清單 10 演示了該映射的工作方式:



    清單 10. 枚舉的映射(Map)
    public void testEnumMap(PrintStream out) throws IOException {
                // Create a map with the key and a String message
                EnumMap<AntStatus, String> antMessages =
                new EnumMap<AntStatus, String>(AntStatus.class);
                // Initialize the map
                antMessages.put(AntStatus.INITIALIZING, "Initializing Ant...");
                antMessages.put(AntStatus.COMPILING,    "Compiling Java classes...");
                antMessages.put(AntStatus.COPYING,      "Copying files...");
                antMessages.put(AntStatus.JARRING,      "JARring up files...");
                antMessages.put(AntStatus.ZIPPING,      "ZIPping up files...");
                antMessages.put(AntStatus.DONE,         "Build complete.");
                antMessages.put(AntStatus.ERROR,        "Error occurred.");
                // Iterate and print messages
                for (AntStatus status : AntStatus.values() ) {
                out.println("For status " + status + ", message is: " +
                antMessages.get(status));
                }
                }
                

    該代碼使用了泛型(generics)(請參閱 參考資料)和新的 EnumMap 構造來建立新映射。而且,枚舉值是通過其 Class 對象提供的,同時提供的還有映射值的類型(在該例中,它只是一個簡單的字符串)。該方法的輸出如清單 11 所示:

    枚舉的 Class 對象?

    您可能已經注意到,清單 10 中的示例代碼實際上表明 Tiger 把枚舉當作類,這可以從 AntStatusClass 對象那里得到證明,該對象不僅可用,而且正被實際使用。這是真的。歸根到底, Tiger 還是把枚舉看成是特殊的類類型。有關枚舉的具體實現細節,請參閱 Java 5.0 Tiger: A Developer's Notebook的第三章(請參閱 參考資料)。



    清單 11. 清單 10 的輸出
    [echo] Running AntStatusTester...
                [java] For status INITIALIZING, message is: Initializing Ant...
                [java] For status COMPILING, message is: Compiling Java classes...
                [java] For status COPYING, message is: Copying files...
                [java] For status JARRING, message is: JARring up files...
                [java] For status ZIPPING, message is: ZIPping up files...
                [java] For status DONE, message is: Build complete.
                [java] For status ERROR, message is: Error occurred.
                







    更進一步

    枚舉也可以與集合結合使用,而且非常像新的 EnumMap 構造,Tiger 提供了一套新的 EnumSet 實現,允許您使用位操作符。另外,可以為枚舉添加方法,用它們實現接口,定義叫作 特定值的類的實體,在該實體中,特定的代碼被附加到枚舉的具體值上。這些特性超出了本文的范圍,但是在其他地方,有詳細介紹它們的文檔(請參閱 參考資料)。







    使用枚舉,但是不要濫用

    學習任何新版語言的一個危險就是瘋狂使用新的語法結構。如果這樣做,那么您的代碼就會突然之間有 80% 是泛型、標注和枚舉。所以,應當只在適合使用枚舉的地方才使用它。那么,枚舉在什么地方適用呢?一條普遍規則是,任何使用常量的地方,例如目前用 switch 代碼切換常量的地方。如果只有單獨一個值(例如,鞋的最大尺寸,或者籠子中能裝猴子的最大數目),則還是把這個任務留給常量吧。但是,如果定義了一組值,而這些值中的任何一個都可以用于特定的數據類型,那么將枚舉用在這個地方最適合不過。

    主站蜘蛛池模板: 蜜桃AV无码免费看永久| 中文亚洲AV片在线观看不卡| 亚洲黄色在线网站| 日韩电影免费观看| 亚洲s色大片在线观看| 日本在线免费观看| 蜜芽亚洲av无码精品色午夜| 亚洲一区免费在线观看| 亚洲免费黄色网址| 毛片免费视频观看| 亚洲av午夜国产精品无码中文字| 韩国18福利视频免费观看| 亚洲av无码一区二区三区天堂| 国产精品麻豆免费版| 黄页免费视频播放在线播放| 亚洲午夜精品一级在线播放放| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 久久精品国产亚洲av麻豆蜜芽 | 妞干网免费视频观看| 亚洲精华液一二三产区| mm1313亚洲国产精品美女| 久久久WWW成人免费精品| 国产AV无码专区亚洲AV男同| 69精品免费视频| 在线观看亚洲AV日韩A∨| 国产一级淫片免费播放| 中文字幕a∨在线乱码免费看 | 亚洲AV无码一区二区三区牲色| 亚洲欧洲日产国码一级毛片 | 免费看美女让人桶尿口| 三级片免费观看久久| 女人被男人躁的女爽免费视频 | 亚洲AV无码一区二区乱子伦| 东方aⅴ免费观看久久av| 亚洲AV午夜福利精品一区二区| 大地资源中文在线观看免费版| 亚洲AV无码专区国产乱码电影 | 免费国产黄线在线观看| 亚洲国产精品无码久久| 国产午夜免费福利红片| AV激情亚洲男人的天堂国语|