程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 在Eclipse 3.1中體驗J2SE 5.0的新特性: 第一部分 :枚舉類型

在Eclipse 3.1中體驗J2SE 5.0的新特性: 第一部分 :枚舉類型

編輯:關於JAVA

J2SE 5.0 (Tiger)的發布是Java語言發展史上的一個重要的裡程碑, 是迄今為止在 Java 編程方面所取得的最大進步.

J2SE 5.0提供了很多令人激動的特性.這些特性包括范型(generics)的支持, 枚舉類型 (enumeration)的支持, 元數據(metadata)的支持, 自動拆箱(unboxing)/裝箱 (autoboxing), 可變個數參數(varargs), 靜態導入(static imports), 以及新的線程架 構(Thread framework).

隨著J2SE 5.0的推出, 越來越多的集成開發環境(IDE)支持J2SE 5.0的開發. 著名的開 源Java IDE Eclipse從3.1M4開始支持J2SE 5.0的開發, 目前最新的版本是3.1RC4.

本系列將介紹J2SE 5.0中三個比較重要的特性: 枚舉類型, 注釋類型, 范型, 並在此 基礎上介紹在如何在Eclipse 3.1開發環境中開發枚舉類型, 注釋類型和范型應用.本文將 介紹枚舉類型.

1. 枚舉類型

1.1枚舉類型簡介

J2SE 5.0 以及之前的JDK有兩種基本方法可以來定義新類型:通過Classes 以及 Interface. 對於大部分面向對象編程來說,這兩種方法看起來似乎足夠了.但是在一些特 殊情況下,這些方法就不適合.例如,我們想定義一個類型 Priority, 它只能接受 High, Medium, Low 三種值. 其他任何值都是非法的.J2SE 5.0 以前的JDK是可以構造這 種類型的,但是需要做很多工作,有可能會帶來如不安全(類型安全性問題???)等潛在問 題,而J2SE 5.0的枚舉類型(Enum) 能避免這些問題.

Eclipse 是JAVA程序員最常用的開發平台,而Eclipse 3.1提供對J2SE 5.0的支持,它 為J2SE 5.0的新功能提供了幫助工具.在對枚舉類型的支持上,它不僅提供了枚舉類型的 創建模板,而且為枚舉類型的各種開發錯誤提供錯誤提示及幫助修改.

本文首先介紹枚舉類型的創建基本概念以及如何在Eclipse 3.1平台上創建枚舉類型, 然後我們通過在Eclipse 3.1開發環境中的例子來說明枚舉類型的應用.

1.2 創建枚舉類型

下面的例子顯示了如何創建一個最基本的枚舉類型:

清單 1. 枚舉類型的定義

public enum Priority {High, Medium, Low };

它包括一個關鍵字enum ,一個新枚舉類型的名字 Priority 以及為Priority定義的 一組值.

在Eclipse 3.1平台上,按照下面步驟來生成枚舉類型:(Eclipse 3.1提供了一個新的 枚舉類型創建向導(wizard)以方便用戶創建枚舉類型)

1) File->New->Other, 模板列表顯示出來.

2) 在模板列表上選中 Java->Enum, 點擊 Next 按鈕

3) 按圖 1填寫每一個域 如下:

圖 1: Eclipse 3.1 枚舉類型創建模板

4) 點擊 Finish 按鈕, 生成Priority 的類(定義???), 並聲明Priority 的每一個值, 如下圖 2所示:(High, Medium, low從何而來???)

圖 2: 枚舉類型Priority

在創建枚舉類型時,注意幾個重要的概念.

所有創建的枚舉類型都擴展於 java.lang.Enum. Enum 是在J2SE 5.0 裡定義的一個新 類, 它本身不是枚舉類型.在創建枚舉類型時,必須用enum 關鍵字,不能直接地定義一 個繼承Enum的類來創建一個枚舉類型,盡管所有創建的枚舉類型實際上都是Enum 的子類. 如果直接繼承Enum, compiler 就會報錯(會導致編譯錯誤).如圖3 所示

圖3. 直接繼承Enum 類

枚舉類型裡定義的每一個值都是枚舉類型的一個實例,比方說High是Priority的 一個實例.枚舉類型又是擴展於Enum. 所以枚舉類型的每一個值聲明時, 缺省時都將映射 到Enum(String name, int ordinal) 構造函數中.換句話說,enum Priority {High, Medium, Low } 的實現是調用了下面的Enum 構造函數:

清單2 映射的構造函數調 用

 new Enum< Priority >("High", 0);
 new Enum< Priority >("Medium", 1);
 new Enum< Priority >("Low", 2);

每一個創建的枚舉類型都是Enum 的子類,除 了上面調用父類 Enum 的構造函數外,枚舉類型可以使用參數為定義一些自己的構造函數 .當聲明值時,只需調用此枚舉類型定義的構造函數,而且不必添加 new 關鍵字.在清單3 裡, Priority 的一個實例生成,這個實例就是High (38).

清單3.其它構造函 數調用

enum Priority {
  High (38),
  Medium (36.5),
  Low (5.2);
  double temperature;
  Priority (double p)
      temperature = p;
  }

另外要強調 的兩點: 一是這些枚舉類型的構造函數都是私有的.它是不能被其它的類或者其它的枚舉 類型調用的. 而且這個私有修飾符是由編譯器自動加的,如果我們定義這些構造函數時,在 前面加上public 修飾符, 就會導致編譯錯誤, 如下圖5所示. 二是變量定義必須在枚舉類 型值定義之後. 上圖中double temperature 必須在枚舉類型值定義完了(分號表示枚舉類 型值定義完了, 如 Low(5.2);) 才能聲明.

圖4. 枚舉類型的構造函數是私有的

在J2SE 5.0以前,當我們實現一個枚舉類時,一般都是把一個整數關聯到此枚舉類的 某一個值的名字,出現的問題是同一個整數可以代表不同枚舉類的值. 下面的例子裡定義 兩個枚舉類 Course and Grade 如下:

清單4.

public class Course {
   public static final int EnglishLit    = 1;
   public static final int Calculus     = 2;
   public static final int MusicTheory   = 3;
   public static final int MusicPerformance = 4;
  }
public class Grade {
  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;
}

如果開發者誤把student1.assignGrade(Grade.A)寫成student1.assignGrade (Course.EnglishList); 在編譯 階段是不能發現問題的,如果用J2SE 5.0 枚舉類型 (enum)可以避免這些問題.

枚舉類型每一個值都是public, static and final的.也就是說,這些值是唯一的而且 一旦定義了是不能被重寫或修改.而且盡管在枚舉類型每一個值聲明時沒有出現static關 鍵字, 實際上值都是靜態的, 而且我們不能在值前面加上static, public,final 修飾 符,否則就會出現下圖 6的錯誤.

圖5 枚舉類型值的錯誤聲明

枚舉類型都實現了java.lang.Comparable,枚舉類型的值是可以比較排序的, 排列順序就是枚舉類型定義這些值的順序.

1.3 枚舉類型的應用

下面各小 節介紹了枚舉類型的各種應用.

1.3.1循環(Iteration)

當我們寫程序時 ,常常遇到對數組或列表裡的每一個對象進行處理的情況.在J2SE 5.0以前,如果要在一 個數組或列表裡進行輪循時,我們的做法比較繁瑣,需要借助java.util.Iterator 類, 如下所示:

清單5:

List priorities = Priority.values().;
for (Iterator iter = priorities.iterator(); iter.hasNext();) {
   Priority p = (Priority) iter.next();
  process(p);
}

現在我們可以通過J2SE 5.0 的for/in loop和枚舉類型一起使用. 這能使以前花很多時間 寫的程序簡單化,如上面清單5的程序可簡化為:

清單6:

for (Priority g: Priority.values()){
  process(g);
}

我們 把上面的偽代碼寫成程序在Eclipse3.1上運行,如下圖所示,在右下控制平台視圖裡顯示 了運行結果.如果看不見控制平台,點擊Window->Other Views->Console, 控制平 台就會出現在右下角.

圖6 枚舉類型在循環中的應用

我們 在使用for/in loop 時要求它的表達式要求必須是數組或者是實現了java.lang.Iterable 的集合,而枚舉類型的values()函數返回的就是一個數組.另外循環變量的聲明必須是在 loop裡, 包括變量類型和變量名.

我們不能在循環裡使用一個在循環之外聲明的變量.這和J2SE 5.0以前for loop 裡用 的循環變量的聲明不同.

1.3.2 轉換(Switch)

我們常用的一種判斷語句就是Switch-case 語句. 在Switch 語句中使用枚舉類型,不 僅能簡化程序,而且增強了程序的可讀性.

清單8.

File1: Task.java
public class Task {
Priority myPriority;
public Task (Priority p) {
   myPriority=p;
}
public Priority getPriority(){
   return myPriority;
}}
File2: TestSwitch.java
public class TestSwitch (
  Task task = new Task(Priority.Medium);
  switch (task.getPriority( )) {
case High:
    //do case High
       break;
    case Midum: // fall through to Low
    case Low:
    //do case Low
       break;
default: throw new AssertionError("Unexpected enumerated value!");
    }
}

在Switch語句裡使用枚舉類型時,一定不能在每一個枚舉類型值的前面加上枚舉類型 的類名,否則編譯器就會報錯(會導致編譯錯誤???). 我們把上面的程序稍作修改,在 case 語句裡加上枚舉類型的類名並運行在Eclipse 3.1 平台上. 我們發現Eclipse 的問 題視圖裡提示case 語句裡枚舉類型值的前面加上枚舉類型的類名是錯誤的, 如下圖8所 示.

圖7: case 語句裡枚舉類型的值

原因是J2SE 5.0的實現要求case 語句裡每一個枚舉類型值是不能有枚舉類型類作為前 綴的.前面談到過每一個枚舉類型的值都是枚舉類型的一個實例.那麼當編譯器編譯case語 句時, 是如何處理這些實例的? 這有兩種情況:如果switch 與枚舉類型定義在同一個編 譯單元, 第一次編譯時一個新表會創建在內存裡. 在這個表裡, 每一個枚舉類型的值都和 它在枚舉類型裡定義的順序關聯起來. 編譯器編譯結果就和下面清單9顯示的的程序很像. 只不過順序號沒有加到程序裡, 而是編譯器在表裡快速查詢. 如果枚舉類型被修改或從定 義,表會被更新.

清單 9:

public class TestSwitch (
  Task task = new Task();
  switch (task.getPriority( )) {
case 0:
    //do case High
       break;
    case 1: // fall through to Low
    case 2:
    //do case Low
       break;
default: throw new AssertionError("Unexpected enumerated value!");
    }
}

還有一種經常出現的情況是 switch 與枚舉類型定義不是在同一個編譯單元.在這種情 況下, 大多數編譯器就會把switch-case 語句翻譯成一系列的if/else 語句:

清單 10:

Priority tmp = task.getPriority( );
if (tmp == High)
//do case High
else if (tmp == Midium)
else if (tmp == Low)
     //do case Low
else {
    throw new AssertionError("Unexpected enumerated value!");
}

1.3.3 Maps of Enum and Sets of Enum

在J2SE 5.0 的java.util 程序包中提供兩個新類:EnumMap 和 EnumSet,這兩個類與 枚舉類型的結合應用可使以前非常繁瑣的程序變得簡單方便.EnumMap 類提供了 java.util.Map 接口的一個特殊實現,該接口中的鍵(key)是一個枚舉類型.

清單 11:. EnumMap 例子

public void test() throws IOException {
   EnumMap<Priority, String> descriptionMessages =
        new EnumMap< Priority, String>( Priority.class);
    descriptionMessages.put(Priority.High,  "High means ...");
      descriptionMessages.put(Priority.Medium, " Medium represents...");
       descriptionMessages.put(Priority.Low, " Low means...");
   for (Priority p : Priority.values( ) ) {
      System.out.println("For priority " + p + ", decription is: " +
             descriptionMessages.get(p));
   }
}

EnumSet 類提供了 java.util.Set 接口的實現,該接口保存了某種枚舉類型的值的集 合.EnumSet的作用類似於特性的集合,或者類似於某個枚舉類型的所有元素的值的子 集.EnumSet 類擁有一系列的靜態方法,可以用這些方法從枚舉類型中獲取單個元素或某 些元素, 下面的程序例子顯示如何這些靜態方法:

清單 12:.EnumSet 例子

public class TestEnumSet {
   public enum ColorFeature {
      RED,BLUE, GREEN, YELLOW,BLACK
    } ;
   public static void main(String[] args) {
   EnumSet allFeatures = EnumSet.allOf(ColorFeature.class);
   EnumSet warmColorFeatures = EnumSet.of(ColorFeature.RED,
                    ColorFeature.YELLOW);
   EnumSet non_warmColorFeatures = EnumSet.complementOf (warmColorFeatures);
     EnumSet notBlack = EnumSet.range(ColorFeature.RED, ColorFeature.YELLOW);

     for (ColorFeature cf : ColorFeature.values()){
       if (warmColorFeatures.contains(cf)) {
         System.out.println("warmColor "+cf.name());
       }
       if (non_warmColorFeatures.contains(cf)) {
         System.out.println("non_WarmColor "+cf.name());
       }
     }
   }
}

我們在Eclipse3.1環境中運行上面的程序,結果如下圖:

圖8: EnumSet 樣例運行結果

1.3.4 枚舉類型的函數定義

在介紹創建枚舉類型中曾提到枚舉類型都是java.lang.Enum的子類. 也就是說, 枚舉 類型都是可編譯的Java 的類,那麼就可以在枚舉類型裡添加構造函數和其它函數,如清 單13裡的getDescription()

清單 13:

public enum ColorFeature {
  RED(0),
  BLUE(0),
  GREEN(300),
  YELLOW(0),
  BLACK(0);
    /** The degree for each kind of color*/
    private int degree;
    ColorFeatures(int degree) {
      this.degree = degree;
    }
    public int getDegree( ) {
       return degree;
    }
     public String getDescription( ) {
       switch(this) {
case RED:    return "the color is red";
case BLUE:   return "the color is blue";
       case GREEN:   return "the color is green";
         case BLACK:   return "the color is black";
         case YELLOW:  return "the color is yellow"
         default:    return "Unknown Color";
       }
     }}

枚舉類型的函數定義的應用是很有用的, 例如可以讓多個枚舉類型實現同一個 interface 來達到程序設計的模式化. 例如一個定義了getDescription ()接口的 interface,讓有同樣需求的不同枚舉類型來實現它.上面的colorFeature 可以實現它, 另 一個FontFeature也可以實現它.

1.3.5 特定於常量的類主體

在上一節裡提到枚舉類型可以定義自己的函數,其實更進一步,枚舉類型的每一個值 都可以實現枚舉類型裡定義的抽象函數,這聽起來很不可思議,我們可以先看下面的例子 .

public enum Priority implements Feature {
  High (38) {
     public void perform() {
       System.out.println("high 38");
     }
   },
   Medium(36.5) {
     public void perform() {
       System.out.println("medium 36.5");
     }
   },
   Low (5.2){
     public void perform() {
       System.out.println("low 5.2");
     }
   };
   public abstract void perform();
   public String getDescription(Priority p) {
     return null;
   }
}

枚舉類型Priority 定義了一個抽象函數perform(),Priority的每一個值都對perform 函數實現了重載,這就是枚舉類型的特定於常量的類主體.在這種情況下,每聲明一個值 ,枚舉類型的一個子類生成,然後生成這個子類的唯一的實例來表示這個值.不同的值, 就有對應的不同的子類.每個子類可以對父類的抽象函數進行重載.我們可以用下面的程序 在Eclipse3.1環境中運行來證明此時3個子類生成.

public class Task {
   Priority myPriority;
public Task (Priority p) {
     myPriority=p;
}
public Priority getPriority(){
   return myPriority;
}
public void test() throws IOException {
   if (myPriority == Priority.High)
        System.out.println(Priority.High.getClass().getName());
      if (myPriority == Priority.Medium)
        System.out.println(Priority.Medium.getClass().getName());
    if (myPriority == Priority.Low)
        System.out.println(Priority.Low.getClass().getName());
}}
public class TestSwitch {
   public static void main(String[] args) {
     Task task = new Task(Priority.High);
   Task task1 = new Task(Priority.Medium);
   Task task2 = new Task(Priority.Low);
   try {
   task.test();
   task1.test();
   task2.test();
   } catch (IOException e) {
   e.printStackTrace();
   }
   }

運行結果如下圖10.

圖9 測試特定於常量的類主體運行結果

由於特定於常量的類主體難理解容易出錯,它的應用比較少,大多數情況下可以用 switch-case 語句替代. 在這裡簡單介紹,僅供參考.

1.4 枚舉類型的小結

使用枚舉類型是很簡單的.它定義一個固定的、封閉的值集合,然後,在需要這些值中 的某一個值時,可以通過它的名稱來指定它,這就是枚舉類型的簡單性.枚舉類型的值就 是枚舉類型的實例,編譯器會確保沒有傳入其他的類型,這就是枚舉類型的安全性.這些 枚舉類型就是類本身,因此,可以對類進行的所有操作同樣可以作用於枚舉類型上.我們 要小心使用構造函數和函數重載方法,不要因為這些特性可用就任意使用它們.比如特定 於常量的類主體,大多情況下可以用Switch語句來代替,更容易讓人理解而且不容易出錯 .我們也看到了Eclipse 3.1平台對枚舉類型的支持,包括提供創建模板,錯誤信息提示等 .總之,枚舉類型的靈活應用能極大的方便和簡化了我們的開發工作.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved