1. 程式人生 > 程式設計 >Java列舉類使用場景及例項解析

Java列舉類使用場景及例項解析

為什麼要用列舉類

什麼場景會用到列舉,比如在表示一週的某一天,一年中的四季,這樣一組常量的時候我們會用到列舉。在Java引入列舉類之前常用一組int常量來表示列舉,這種方式稱為int列舉模式(int enum pattern)。

private static final int MONDAY = 1;
private static final int TUESDAY = 2;
private static final int WEDNESDAY = 3;
private static final int THURSDAY = 4;

private static final int CODE_START = 1;

private static final int CODE_STATUS = 2;
private static final int CODE_STOP = 3;

這種我們非常習慣的模式其實存在著很多不足和問題,

  • int列舉組不具備名稱空間的能力,當表示具有相同命名常量時,需要新增字首避免衝突
  • int表示的列舉值不具有描述性,需要遍歷判斷具體的值並新增描述
  • int列舉模式不具有安全性,此外int型別是編譯時常量,如果與int列舉常量關聯的值發生變化,必須重新編譯,不重新編譯雖然不影響執行,但是準確性已經不能保證

即便是升級為用String來表示列舉值,String列舉模式(String enum pattern),但這樣同樣存在其他問題,

初學者容易直接把字串常量硬編碼到程式碼中,不使用對應的常量欄位(filed)名,一旦書寫錯誤,編譯器無法檢查,但在執行時會報出異常

String列舉模式會存在一定的效能問題,涉及到字串的比較操作

因此Java引入了列舉型別解決int和String列舉模式帶來的諸多不足,列舉型別保證了編譯時的型別安全,列舉型別有自己獨立的名稱空間,列舉型別便於擴充套件,可以新增方法和域實現其他的外部介面。

如何使用列舉類

建立列舉類

Java中列舉是一種特殊的引用型別,是類(Class)的一種,JDK1.5中開始引入列舉型別,在Java中使用enum關鍵字來宣告列舉類,列舉類編譯後預設繼承了java.lang.Enum,因此列舉類不能在繼承其他類,列舉一般用來宣告某一特定型別的有窮集合,如用列舉表示四季

public enum Season {
  SPRING,SUMMER,FALL,WINTER
}

列舉類API

參考JDK api 1.8.CHM,可以看到列舉類的常用api如下:

name public final String name()

返回此列舉常量的名稱,與其列舉宣告中宣告的完全相同。 大多數程式設計師應該使用toString()方法,因為toString方法可能會返回一個更加使用者友好的名稱。 該方法主要用於專門的情況,其中正確性取決於獲得確切的名稱,這從釋出到釋出不會有所不同。

ordinal public final int ordinal()

返回此列舉常數的序數(其列舉宣告中的位置,其中初始常數的序數為零)。 大多數程式設計師將不會使用這種方法。 它被設計為使用複雜的基於列舉的資料結構,如EnumSet和EnumMap 。

toString public String toString()

返回宣告中包含的此列舉常量的名稱。 該方法可以被覆蓋,儘管它通常不是必需或不可取的。 當一個更“程式設計師友好”的字串形式存在時,列舉型別應該覆蓋此方法。

重寫: toString 在 Object

compareTo public final int compareTo(E o)

將此列舉與指定的物件進行比較以進行訂購。 返回一個負整數,零或正整數,因為該物件小於,等於或大於指定物件。 列舉常數僅與相同列舉型別的其他列舉常量相當。 該方法實現的自然順序是宣告常量的順序。

Specified by: compareTo 在介面Comparable<E extends Enum<E>

引數 :o - 要比較的物件。

結果 :負整數,零或正整數,因為該物件小於,等於或大於指定物件。

getDeclaringClass public final Class<E> getDeclaringClass()

返回與此列舉常量的列舉型別相對應的Class物件。 當且僅當e1.getDeclaringClass()== e2.getDeclaringClass())時,兩個列舉常量e1和e2具有相同的列舉型別。 (此方法返回的值可能與使用常量特定類體的列舉常數Object.getClass()方法返回的值不同)

結果:該類物件對應於此列舉常量的列舉型別

valueOf public static <T extends Enum<T>> T valueOf(Class <T> enumType,String name)

返回具有指定名稱的指定列舉型別的列舉常量。 該名稱必須與用於宣告此型別的列舉常量的識別符號完全一致。 (不允許使用外來空白字元。)
請注意,對於特定列舉型別T ,可以使用該列舉上隱式宣告的public static T valueOf(String)方法,而不是使用此方法將名稱對映到

相應的列舉常量。 列舉型別的所有常量可以通過呼叫該型別的隱式public static T[] values()方法來獲得。

values

此方法並未在API中提供,返回列舉型別所有物件例項,返回值列舉型別的陣列。

列舉應用案例

上面簡單描述瞭如何宣告一個列舉類,這裡結合實際應用場景描述列舉的其他用法

單例設計模式

說到單例模式很多人會比較熟悉懶漢、餓漢等常見的單例書寫模式,用列舉表示列舉還是比較少見的,對於單例設計模式的多種寫法,單元素的列舉型別已經成為實現Singleton的最佳方法。首先回顧下單例設計模式要求滿足的特點:

  • 構造方法私有化;
  • 例項化的變數引用私有化;
  • 獲取例項的方法共有。
public enum Singleton {
  INSTANCE;
  public Singleton getInstance(){
    return INSTANCE;
  }
}

使用列舉方式建立單例的好處:

  • 避免反射攻擊
  • 避免序列化問題

有窮物件集合

列舉型別中的構造器預設私有化,只能新增private修飾或者不新增

列舉型別中定義的抽象方法必須被所有常量中的具體方法所覆蓋,特定於常量的方法實現可以結合特定於常量的資料結合起來

用列舉表示加減乘除的操作

public enum Operation {
  PLUS("+","加法"){
    public double apply(double x,double y){
      return x + y;
    }
  },MINUS("-","減法"){
    public double apply(double x,double y){
      return x - y;
    }
  },TIMES("*","乘法"){
    public double apply(double x,double y){
      return x * y;
    }
  },DIVIDE("/","除法"){
    public double apply(double x,double y){
      return x / y;
    }
  };

  private final String symbol;
  private final String operName;

  public String getSymbol() {
    return symbol;
  }
  public String getOperName() {
    return operName;
  }
  Operation(String symbol,String operName){
    this.symbol = symbol;
    this.operName = operName;
  }
  public abstract double apply(double x,double y);
}

呼叫列舉中的方法

public class TestOpera {
  public static void main(String[] args) {
    double x = 1;
    double y = 1;
    for(Operation operate : Operation.values()){
      System.out.println(
      operate.getOperName()+":"+x+operate.getSymbol()+y+" = "+operate.apply(x,y)
      );
    }
  }
}

輸出結果

加法:1.0 + 1.0 = 2.0
減法:1.0 - 1.0 = 0.0
乘法:1.0 * 1.0 = 1.0
除法:1.0 / 1.0 = 1.0

引入列舉型別,不僅可以描述列舉本身,還可以新增描述性字串,甚至給每個物件新增結合特有常量的行為,也不用考慮其他安全性為題。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。