天天看點

Java Review (十七、面向對象----枚舉類)枚舉類入門枚舉類的成員變量、方法和構造器實作接口的枚舉類包含抽象方法的枚舉類

文章目錄

enum 的全稱為 enumeration, 是 JDK 1.5 中引入的新特性。

在Java中,被 enum 關鍵字修飾的類型就是枚舉類型。

Java 5 新增了 一個 enum 關鍵宇 (它與 class 、 interface 關鍵字的地位相同),用以定義枚舉類 。 枚舉類是一種特殊的類,它一樣可以有自己的成員變量、方法,可以實作一個或者多個接口,也可以定義自己的構造器 。一個 Java 源檔案中最多隻能定義一個 public 通路權限的枚舉類,且該Java 源檔案也必須和該枚舉類的類名相同。

它與普通類有如下簡單差別。

  • 枚舉類可以實作一個或多個接口,使用 enum 定義的枚舉類預設繼承了 java.lang.Enum 類,而不是預設繼承 Object 類,是以枚舉類不能顯式繼承其他父類。其中 java.lang.Enum 類實作了java.lang.Serializable 和 java.lang.Comparable 兩個接口 。
  • 使用 enum 定義、非抽象的枚舉類預設會使用 final 修飾,是以枚舉類不能派生子類 。
  • 枚舉類的構造器隻能使用 private 通路控制符 , 如果省略了構造器的通路控制符,則預設使用private 修飾 ; 如果強制指定通路控制符,則隻能指定 private 修飾符 。
  • 枚舉類的所有執行個體必須在枚舉類的第一行顯式列出,否則這個枚舉類永遠都不能産生執行個體 。 列出這些執行個體時,系統會自動添加 public static final 修飾,無須程式員顯式添加 。枚舉類預設提供了 一個 valuesO方法,該方法可以很友善地周遊所有的枚舉值 。

下面程式定義了 一個 SeasonEnum 枚舉類 。

SeasonEnum.java

public enum SeasonEnum {
    //在第一行列出 4 個枚舉執行個體
    SPRING, SUMMER ,FALL,WINTER ;
}      

定義枚舉類時,需要顯式列出所有的枚舉值,如上面的SPRING,SUMMER,FALL,WINTER所示,所有的枚舉值之間以英文逗号 , 隔開,枚舉值列舉結束後以英文分号作為結束 。 這些枚舉值代表了該枚舉類的所有可能的執行個體 。

如果需要使用該枚舉類的某個執行個體,則可使用 EnumClass.variable 的形式,如 SeasonEnum. SPRING 。

EnumTest.java

public class EnumTest {
    public void judge(SeasonEnum s) {
      // switch 語句裡的表達式可以是枚舉值
      switch (s) {
        case SPRING:
          System.out . println( " 春暖花開,正好踏青") ;
          break ;
        case SUMMER :
          System . out.println( " 夏日炎炎,适合遊泳 " ) ;
          break;
        case FALL:
         System.out.println( " 秋高氣爽 , 進補及時 " ) ;
         break;
        case WINTER:
         System . out.println( " 冬日雪飄,國爐賞雪 " );
         break;
      }
    }  
    
    public static void main(String[] args){
     //枚舉類預設有一個 values ()方法,傳回該枚舉類的所有執行個體
      for (SeasonEnum s : SeasonEnum.values()) {
        System.out.println(s) ;
      }
      //使用枚舉執行個體時,可通過 EnumClass . variable 形式來通路
      new EnumTest().judge(SeasonEnum.SPRING) ;
    }

}      

所有的枚舉類都繼承 了 java.lang.Enum 類,是以枚舉類可以直接使用java .lang.Enum 類中所包含的方法 。 java . lang .Enum 類中提供了如下幾個方法 :

Java Review (十七、面向對象----枚舉類)枚舉類入門枚舉類的成員變量、方法和構造器實作接口的枚舉類包含抽象方法的枚舉類

枚舉類也是一種類,隻是它是一種比較特殊的類,是以它一樣可以定義成員變量、方法和構造器。

下面程式将定義一個 Gender 枚舉類,該枚舉類裡包含了 一個 name 執行個體變量。

Gender.java

public enum Gender {
    MALE , FEMALE;
    //定義一個 public 修飾的執行個體變量
    public String name;
}      

通過如下程式來使用該枚舉類 。

GenderTest.java

public class GenderTest {
    public static void main(String[] args) {
        //通過 Enum 的 valueOf ()方法來擷取指定枚舉類的枚舉值
        Gender g = Enum.valueOf(Gender.class , "FEMALE");
        //直接為枚舉值的 name 執行個體變量賦傻
        g.name = "女";
        //直接通路枚舉值的問me 執行個體變量
        System.out.println(g + "代表 :" + g.name);
    }

}      

正如前面提到的, Java 應該把所有類設計成良好封裝的類 ,是以不應該允許直接通路 Gender 類的name 成員 變量 ,而是應該通過方法來控制對 name 的通路 。 否則可能出現很混亂的情形,例如上面程式恰好設定了 g.name =“女”,要是采用 g.name = "男 ",那程式就會非常混亂了 ,可能出現 FEMALE 代表男的局面。可以按如下代碼來改進 Gender 類的設計 。

public enum Gender {
    MALE , FEMALE ;
    //私有化,免其他程式直接通路該 name 成員變量
    private String name ;
    public void setName(String name){
     switch (this){
      case MALE :
       //MALE 枚舉值的 name 變量則隻能設定為"男 "
       if (name.equals("男")) {
         this.name = name;
       }else{
         System . out . println("參數錯誤") ;
         return;
       }  
      break ;
      case FEMALE:
       // FEMALE 枚舉值的 name 變量隻能設定為"女" 
       if (name.equals("女") ) {
        this.name = name;
       }else {
        System.out.println ("參數錯誤 ") ;
        return;
       } 
      break;
     }
    }
    public String getName() {
     return this.name;
    }
}      

枚舉類也可以實作一個或多個接口 。與普通類實作一個或多個接口完全一樣 , 枚舉類實作一個或多個接口時, 也需要實作該接口所包含的方法。下面程式定義了 一個 GenderDesc 接口。

GenderDesc.java

public interface GenderDesc{
  void info () ;
}        

在上面 GenderDesc 接口中定義了 一個 infoO方法,下面的 Gender 枚舉類實作了該接口,并實作了該接口裡包含的 info()方法 。 下面是 Gender 枚舉類的代碼 。

public enum Gender implements GenderDesc{
    MALE , FEMALE ;
    private String name ;
    public void setName(String name){
     switch (this){
      case MALE :
       if (name.equals("男")) {
         this.name = name;
       }else{
         System . out . println("參數錯誤") ;
         return;
       }  
      break ;
      case FEMALE:
       if (name.equals("女") ) {
        this.name = name;
       }else {
        System.out.println ("參數錯誤 ") ;
        return;
       } 
      break;
     }
    }
    public String getName() {
     return this.name;
    }
    
   //實作接口info()方法
   public void info(){
     System.out.println("這是一個用于定義性别的枚舉類 " );
   }  
   
}      

枚舉類和普通類一樣使用 implements 實作接口,井實作接口裡包含的抽象方法 。

如果由枚舉類來實作接口裡的方法,則每個枚舉值在調用該方法時都有相同的行為方式(因為方法體完全一樣) 。

如果需要每個枚舉值在調用該方法時呈現出不 同 的行為方式 , 則可以讓每個枚舉值分别來實作該方法 , 每個枚舉值提供不同的實作方式,進而讓不同的枚舉值調用該方法時具有不同的行為方式 。在下面的 Gender 枚舉類中,不同的枚舉值對 info()方法的實作各不相同 。

public enum Gender implements GenderDesc{
    //此處的枚舉值必須調用對應的構造器來建立
    MALE ("男"){
    //花括号部分實際上是一個類體部分
    //并不是直接建立Gendar枚舉類的執行個體
    //而是相當于建立 Gender 的匿名子類的執行個體
      public void info(){
        System.out.println("這個枚舉值代表男性") ;
      } 
     }  
    
   FEMALE(" 女"){
     public void info(){
       System.out.println("這個枚舉值代表女性") ;
     }
   }  
   
    private String name ;
    public void setName(String name){
     switch (this){
      case MALE :
       if (name.equals("男")) {
         this.name = name;
       }else{
         System.out.println("參數錯誤") ;
         return;
       }  
      break ;
      case FEMALE:
       if (name.equals("女") ) {
        this.name = name;
       }else {
        System.out.println ("參數錯誤 ") ;
        return;
       } 
      break;
     }
    }
    public String getName() {
     return this.name;
    } 
   
}      

假設有一個 Operation 枚舉類,它的 4 個枚舉值 PLUS, MINUS, TIMES, DIVIDE 分别代表加、減、乘、除 4 種運算,該枚舉類需要定義一個 eval()方法來完成計算 。

從上面描述可以看出, Operation 需要讓 PLUS 、 MINUS 、 TIMES 、 DIVIDE 四個值對 eval()方法各有不同的實作 。此時可考慮為 Operation 枚舉類定義一個 evalO抽象方法,然後讓 4 個枚舉值分别為 eval()提供不同的實作 。 例如如下代碼 。

Operation.java

public enum Operation {
   //定義每個枚舉值時實作抽象方法
    PLUS{
      public double eval(double x , double y) {
        return x + y;
      }  
    },  
    MINUS{
      public double eval(double x , double y) {
        return x - y;
      }
    },      
    TIMES{
      public double eval(double x , double y) {
          return x * y;
      }
    } ,
    DIVIDE{  
      public double eval(double x , double y) {
        return x / y;
      }
    };  
    // 為枚舉類定義一個抽象方法
    // 這個抽象方法由不同的枚舉值提供不同的實作
    public abstract double eval(double x , double y);
    public static void main(String[] args) {
        System.out.println (Operation.PLUS.eval (3 , 4)) ;
        System.out.println(Operation.MINUS.eval(5,4)) ;
        System.out.println(Operation.TIMES.eval(5,4)) ;
        System.out.println(Operation.DIVIDE.eval(5,4));
    }   

}      

編譯上面程式會生成 5 個 class 檔案,其中Operation 對應一個 class 檔案,它的 4 個匿名内部子類,分别各對應一個 class 檔案 。

枚舉類裡定義抽象方法時不能使用 abstract 關鍵字将枚舉類定義成抽象類(因為系統自動會為它添加 abstract 關鍵宇),但因為枚舉類需要顯式建立枚舉值,而不是作為父類,是以定義每個枚舉值時必須為抽象方法提供實作,否則将出現編譯錯誤 。

參考:

【1】:《瘋狂Java講義》

【2】:

https://juejin.im/post/5c13133be51d456fac740c98