Enum.values()方法从何而来?

8
我正在查看文档和源代码,因为我想确定values()将始终按照枚举值的声明顺序返回一个数组。结果,据我所知,这在文档中似乎不是这样。
我检查了Enum类的源代码,但没有发现(有一个相关的私有“getValues”方法)。
因此,我猜测编译器/解释器可能已经创建了一个扩展Enum类的类,如下所示的声明:
public static enum MyEnum

那么values()方法在编译期间是否也被静态地转换为硬编码数组?还是它实际上是在运行时调用的方法,如果是这样,它在哪里定义?

如果你非常在意这个问题,那么很可能在枚举声明中移动一些内容会导致某些东西出错。这就非常脆弱了。所以我认为,如果你在意这个问题,你可能有一个奇怪的设计。为什么它们的顺序对你很重要呢? - Mark Peters
@Mark Peters - 因为我在多个地方使用有序菜单项的枚举。当然,我可以创建某种排序函数,以某种顺序返回它们...但为什么呢?如果我移动它们,它不会出错,只会改变它们在整个应用程序中呈现的顺序。 - DougW
4个回答

13
< p > values() 方法是 enum 类型定义的一部分。不要与 Enum 基类混淆。正式定义在 JLS 第8.9节 中,其中指定返回的顺序与声明顺序匹配。


完美,这就是我想到的情况。谢谢! - DougW

2

通过反汇编枚举得到的字节码,可以看出枚举上的values()方法只是返回一个包含所有已声明枚举常量的private static数组的副本。这个名为ENUM$VALUES的数组在静态初始化块中填充。

DaysOfTheWeek.java

public enum DaysOfTheWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

DaysOfTheWeek.java反汇编

在静态块之后,字节码标记为0-92的部分初始化了枚举常量,字节码标记为94-139的部分将这些常量放入一个数组中,而字节码标记为140的部分则将该数组赋值给类的ENUM$VALUES静态字段。在values()方法中的代码仅通过调用System.arraycopyENUM$VALUES字段上创建了一个副本并返回该副本。

Compiled from "DaysOfTheWeek.java"
public final class DaysOfTheWeek extends java.lang.Enum{
public static final DaysOfTheWeek MONDAY;

public static final DaysOfTheWeek TUESDAY;

public static final DaysOfTheWeek WEDNESDAY;

public static final DaysOfTheWeek THURSDAY;

public static final DaysOfTheWeek FRIDAY;

public static final DaysOfTheWeek SATURDAY;

public static final DaysOfTheWeek SUNDAY;

static {};
  Code:
   0:   new #1; //class DaysOfTheWeek
   3:   dup
   4:   ldc #18; //String MONDAY
   6:   iconst_0
   7:   invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   10:  putstatic   #23; //Field MONDAY:LDaysOfTheWeek;
   13:  new #1; //class DaysOfTheWeek
   16:  dup
   17:  ldc #25; //String TUESDAY
   19:  iconst_1
   20:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   23:  putstatic   #26; //Field TUESDAY:LDaysOfTheWeek;
   26:  new #1; //class DaysOfTheWeek
   29:  dup
   30:  ldc #28; //String WEDNESDAY
   32:  iconst_2
   33:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   36:  putstatic   #29; //Field WEDNESDAY:LDaysOfTheWeek;
   39:  new #1; //class DaysOfTheWeek
   42:  dup
   43:  ldc #31; //String THURSDAY
   45:  iconst_3
   46:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   49:  putstatic   #32; //Field THURSDAY:LDaysOfTheWeek;
   52:  new #1; //class DaysOfTheWeek
   55:  dup
   56:  ldc #34; //String FRIDAY
   58:  iconst_4
   59:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   62:  putstatic   #35; //Field FRIDAY:LDaysOfTheWeek;
   65:  new #1; //class DaysOfTheWeek
   68:  dup
   69:  ldc #37; //String SATURDAY
   71:  iconst_5
   72:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   75:  putstatic   #38; //Field SATURDAY:LDaysOfTheWeek;
   78:  new #1; //class DaysOfTheWeek
   81:  dup
   82:  ldc #40; //String SUNDAY
   84:  bipush  6
   86:  invokespecial   #19; //Method "<init>":(Ljava/lang/String;I)V
   89:  putstatic   #41; //Field SUNDAY:LDaysOfTheWeek;
   92:  bipush  7
   94:  anewarray   #1; //class DaysOfTheWeek
   97:  dup
   98:  iconst_0
   99:  getstatic   #23; //Field MONDAY:LDaysOfTheWeek;
   102: aastore
   103: dup
   104: iconst_1
   105: getstatic   #26; //Field TUESDAY:LDaysOfTheWeek;
   108: aastore
   109: dup
   110: iconst_2
   111: getstatic   #29; //Field WEDNESDAY:LDaysOfTheWeek;
   114: aastore
   115: dup
   116: iconst_3
   117: getstatic   #32; //Field THURSDAY:LDaysOfTheWeek;
   120: aastore
   121: dup
   122: iconst_4
   123: getstatic   #35; //Field FRIDAY:LDaysOfTheWeek;
   126: aastore
   127: dup
   128: iconst_5
   129: getstatic   #38; //Field SATURDAY:LDaysOfTheWeek;
   132: aastore
   133: dup
   134: bipush  6
   136: getstatic   #41; //Field SUNDAY:LDaysOfTheWeek;
   139: aastore
   140: putstatic   #43; //Field ENUM$VALUES:[LDaysOfTheWeek;
   143: return

public static DaysOfTheWeek[] values();
  Code:
   0:   getstatic   #43; //Field ENUM$VALUES:[LDaysOfTheWeek;
   3:   dup
   4:   astore_0
   5:   iconst_0
   6:   aload_0
   7:   arraylength
   8:   dup
   9:   istore_1
   10:  anewarray   #1; //class DaysOfTheWeek
   13:  dup
   14:  astore_2
   15:  iconst_0
   16:  iload_1
   17:  invokestatic    #51; //Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
   20:  aload_2
   21:  areturn

public static DaysOfTheWeek valueOf(java.lang.String);
  Code:
   0:   ldc #1; //class DaysOfTheWeek
   2:   aload_0
   3:   invokestatic    #59; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   6:   checkcast   #1; //class DaysOfTheWeek
   9:   areturn

}

1

您提供的链接(其中包含私有getValues方法)来自Apache Harmony(版本6,这是一个开源的Java SE)。他们对Oracle的Enum类有不同的实现(没有私有的getValues方法)。

在编写本文时,Oracle Java(版本1.6.0-21)具有valueOf(Class<T> enumType,String name)。以下是它的实现:

 /**
     * Returns the enum constant of the specified enum type with the
     * specified name.  The name must match exactly an identifier used
     * to declare an enum constant in this type.  (Extraneous whitespace
     * characters are not permitted.) 
     *
     * @param enumType the <tt>Class</tt> object of the enum type from which
     *      to return a constant
     * @param name the name of the constant to return
     * @return the enum constant of the specified enum type with the
     *      specified name
     * @throws IllegalArgumentException if the specified enum type has
     *         no constant with the specified name, or the specified
     *         class object does not represent an enum type
     * @throws NullPointerException if <tt>enumType</tt> or <tt>name</tt>
     *         is null
     * @since 1.5
     */
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum const " + enumType +"." + name);
    }

本质上,这是Apache实现他们的valueOf不同的方式。


Enum(或enum)有一个名为 values() 的公共静态方法,该方法返回在enum内声明的枚举值常量。这是由编译器填充的。


2
我还是感到毛骨悚然,看到“Oracle的Enum类”或“Oracle Java”。这让我想到他们为在Oracle数据库中使用而制作的JVM。 - Mark Peters
暂时不要更改名称,直到Oracle为Java做出重大贡献。 - irreputable
哈哈,如果你们还没有注意到,大部分的 sun.com 网站现在都变成了 oracle.com。我们也不妨跟进一下... - Buhake Sindi
是的,我刚刚用谷歌搜了一下。我正在开发 Android,它也有那个getValues方法。 - DougW

1
我想补充一下Devon的回答,values()方法是根据定义由编译器添加的。
来自tutorial中的枚举:

编译器在创建枚举时会自动添加一些特殊的方法。例如,它们有一个静态的values方法,该方法按声明顺序返回包含枚举所有值的数组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接