天天看點

詳解Java枚舉類型(Enum)中的方法Enum抽象類常見方法枚舉類型的構造函數再論編譯器插入的靜态方法總結

在上篇文章中,我們對Java中的枚舉類進行了詳細的介紹。

對于Enum還不了解的小夥伴,可以先預習《Java中的枚舉類型(Enum)詳解》一文。

通過反編譯,我們知道Java枚舉類會在編譯之後轉化為一個繼承了java

.lang.Enum的類,而我們定義的每個枚舉值都會在類的初始化階段被執行個體化為我們所定義的枚舉類的一個對象。

在枚舉類被編譯之後,有一些方法是編譯器在編譯階段寫入的,那這些方法有什麼特點?枚舉類中還有一些繼承來的方法,它們又有哪些?枚舉類中的枚舉值是在編譯階段被建立為對象,那構造函數又在哪?

這篇文章我們将詳細分析。

Enum抽象類常見方法

我們上篇文章已經講過,枚舉類實際上繼承了Enum抽象類,是以Enum抽象類是所有枚舉類型的基本類,下面是它的常見方法。

  • ordinal()方法:該方法擷取的是枚舉變量在枚舉類中聲明的順序,下标從0開始,如日期中的MONDAY在第一個位置,那麼MONDAY的ordinal值就是0,如果MONDAY的聲明位置發生變化,那麼ordinal方法擷取到的值也随之變化,注意在大多數情況下我們都不應該首先使用該方法,畢竟它總是變幻莫測的。
  • compareTo(E o)方法:則是比較枚舉的大小,注意其内部實作是根據每個枚舉的ordinal值大小進行比較的。
  • name()方法與toString():幾乎是等同的,都是輸出變量的字元串形式。
  • getDeclaringClass(): 傳回該枚舉變量所在的枚舉類。

需要再次說明的是,以上的方法都是Enum抽象類的方法,會被Enum的對象繼承,而不是Enum的靜态方法。而最終枚舉值被執行個體化成了Enum對象,是以,枚舉值擁有以上的方法。

這一塊比較簡單,我們直接舉例子說明:

首先我們定義一個最簡單的枚舉類:

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

之後我們在定義一個附帶屬性的枚舉類:

public enum StatusEnum {

    DOING("進行中", "DOING"),
    DONE("已完成", "DONE"),
    CANCELLED("已取消", "CANCELLED");

    private String name;
    private String status;

    StatusEnum(String name, String status) {
        this.name = name;
        this.status = status;
    }

   // 省略name/status的getter/setter
           

接下來,我們寫方法進行試驗:

public void doTest() {
    Season[] Seasons = Season.values();
    for (Season Season : Seasons) {
        System.out.print("toString:" + Season.toString());
        System.out.print("; name:" + Season.name());
        System.out.println("; ordinal:" + Season.ordinal());
    }

    System.out.println("----");
    System.out.println(Season.valueOf("AUTUMN").name());
    System.out.println("----");
    Class clazz = Season.SUMMER.getDeclaringClass();
    System.out.println(clazz.getName());
    System.out.println("----");
    System.out.println(StatusEnum.CANCELLED.name());// 這是Enum自有的方法
    System.out.println(StatusEnum.CANCELLED.getName()); // 這是我們寫的getter方法
}
           

最後結果:

toString:SPRING; name:SPRING; ordinal:0
toString:SUMMER; name:SUMMER; ordinal:1
toString:AUTUMN; name:AUTUMN; ordinal:2
toString:WINTER; name:WINTER; ordinal:3
----
AUTUMN
----
com.test.constant.Season
----
CANCELLED
已取消
           

我們可以看到,對于每個枚舉值,可以調用上述的繼承自Enum抽象類的方法。

枚舉類型的構造函數

既然枚舉值是由編譯器建立為枚舉類型的執行個體,那它必然調用了構造函數。那該函數在哪呢?我們能不能調用呢?

其實該構造函數也在Enum抽象類中。

/**
 - Sole constructor.  Programmers cannot invoke this constructor.
 - It is for use by code emitted by the compiler in response to
 - enum type declarations.
 *
 - @param name - The name of this enum constant, which is the identifier
 -               used to declare it.
 - @param ordinal - The ordinal of this enumeration constant (its position
 -         in the enum declaration, where the initial constant is assigned
 -         an ordinal of zero).
 */
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

           

我們特意将方法注釋也保留了下來,可以看到,該方法隻能由編譯器調用,開發人員無法調用。是以說,我們還是不要操心了,隻需要定義好枚舉類型,剩下的交給編譯器。

再論編譯器插入的靜态方法

我們知道values()方法和valueOf(String s)方法是由編譯器插入到枚舉類中的靜态方法。這總讓人覺得怪異。而同時,我們知道枚舉類型中的每一個枚舉值也在編譯階段被聲明為了一個枚舉類。關于這幾點,我們在上一篇文章中已經詳細分析,大家可以回上篇文章找回記憶。我們直接貼出通過位元組碼推出的代碼,如下:

public final class com.test.constant.Season extends java
.lang.Enum<com.test.constant.Season> {
  public static final com.test.constant.Season SPRING;
  public static final com.test.constant.Season SUMMER;
  public static final com.test.constant.Season AUTUMN;
  public static final com.test.constant.Season WINTER;

  private static final com.test.constant.Season[] $VALUES;

  public static com.test.constant.Season[] values(){
        return (Season[])$VALUES.clone();
  }

  public static com.test.constant.Season valueOf(String s){
        return (Season)Enum.valueOf(java/lang/String, s);
    }

  private com.test.constant.Season(String s, int i){
    super(s,i);
  }

  static {
    SPRING = new Season("SPRING", 0);
    SUMMER = new Season("SUMMER", 1);
    AUTUMN = new Season("AUTUMN", 2);
    WINTER = new Season("WINTER", 3);
        $VALUES = (new Season[] {
            SPRING, SUMMER, AUTUMN, WINTER
        });
    }
}
           

有人會這麼認為:

  • 原來的Season枚舉類中被編譯器插入了values()方法和valueOf(String s)方法,是以能夠正常調用Season.values()
  • 如果我們使用某個枚舉值,如Season.AUTUMN向上轉型成為Season枚舉類,則無法調用values()方法和valueOf(String s)方法

我對此存疑,因為無論是原生的Season枚舉類還是Season.AUTUMN向上轉型成的Season枚舉類,本質上是同一個枚舉類。是以,都應該可以調用values()方法和valueOf(String s)方法。

對此,我們進行驗證:

public class MainTest {
    public static void main(String[] args) {
        System.out.println("原生的枚舉類:" + Season.class.getName());
        System.out.println(Season.values()[1]);

        Season s = Season.AUTUMN;
        System.out.println("枚舉值向上轉型出的枚舉類:" + s.getClass().getName());
        System.out.println(s.values()[1]);
    }
}
           

得到如下輸出:

原生的枚舉類:com.test.constant.Season
SUMMER
枚舉值向上轉型出的枚舉類:com.test.constant.Season
SUMMER
           

證明了筆者的猜測。

總結

通過該文章,我們對枚舉類中的方法進行了全面的了解:

  • 枚舉類在編譯階段會被編譯器插入一些靜态方法
  • 枚舉類本身有個隻有編譯器能夠調用的構造方法,編譯器會使用該方法将枚舉值執行個體化為枚舉類型的對象
  • 枚舉值被執行個體化後,繼承了衆多java.lang.Enum中的方法

這樣,通過《Java中的枚舉類型(Enum)詳解》和本篇文章,我們對枚舉類型的原理和方法有了詳細的了解。接下來,我們還會有一篇文章介紹枚舉類型的使用,進而從原理、特性、使用三個方面對枚舉類型進行詳細的介紹,歡迎繼續關注。