Java中Enum类下的values()方法以及由来

发布于:2023-09-08 ⋅ 阅读:(61) ⋅ 点赞:(0)

 

枚举类中的元素是无法通过下标值来访问的,如果你想指定访问枚举类中的某个值,你只能直接写出它们的值,除此之外,别无他法。但是枚举类有一个values()方法,这个方法可以将枚举类转换成一个枚举类型的数组,转换成数组之后我们就可以通过下标来访问我们的枚举类中的值。比如下面的代码:

 
enum Direction {
 
    LEFT, RIGHT, UP, DOWN
 
}

    这里面有四个值,如果我们想通过下标来访问的话,就必须进行如下的操作:


 

 
Direction dirs[] = Direction.values();
 
for (int i = 0; i < dirs.length; i++) {
 
     System.out.println(dirs[i]);
 
}

这个操作有什么用呢?主要用途就是从这个枚举类中选取一个随机值,具体的代码如下:

 
Random r = new Random();
 
int ri = r.netInt(dirs.length);
 
Direction dir = dirs[ri];
​​​​​​

上面这段代码的用处很广,比如在游戏中,指定一个随机方向;在抽奖中,选择一个随机的奖品等等“需要随机对象的地方”。那么为什么要单独说这个方法呢?因为在API文档中,Enum类的这个方法你是查不到,无论是它的父类中,还是他实现的接口中。
    关于Enum类中values方法的由来,有人做了比较深入的论证,具体见下面
Java中Enum类下的values()方法的由来

Java中Enum类下的values()方法的由来

有如下Enum类:

public enum Test{
 A,B,C
}

该类下有values()方法,但查API可知道java.lang.Enum<ElementType>下根本没有这个方法,该方法在java.lang.annotation.ElementType类下(public enum ElementType extends Enum<ElementType>),我们先看看编译后,用javap命令查看编译后的内容:

C:\>javap Test
Compiled from "Test.java"
public final class Test extends java.lang.Enum<<FONT color=#fe3824>Test> {
  public static final Test A;
  public static final Test B;
  public static final Test C;
  public static Test[] values();
  public static Test valueOf(java.lang.String);
  static {};
}

可以看出,貌似编译后Test是实现了java.lang.annotation.ElementType这个接口,再看其字节码:

 C:\>javap -v Test
Classfile /C:/Test.class
  Last modified 2012-11-22; size 782 bytes
  MD5 checksum 17335f37d5ac9d7fe7d5544782cf7088
  Compiled from "Test.java"
public final class Test extends java.lang.Enum
  Signature: #32                         // Ljava/lang/Enum;
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref          #4.#35        //  Test.$VALUES:[LTest;
   #2 = Methodref         #36.#37       //  "[LTest;".clone:()Ljava/lang/Objec
t;
   #3 = Class             #20           //  "[LTest;"
   #4 = Class             #38           //  Test
   #5 = Methodref         #14.#39       //  java/lang/Enum.valueOf:(Ljava/lang
/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref         #14.#40       //  java/lang/Enum."":(Ljava/lan
g/String;I)V
   #7 = String            #15           //  A
   #8 = Methodref         #4.#41        //  Test."":(Ljava/lang/String;I
)V
   #9 = Fieldref          #4.#42        //  Test.A:LTest;
  #10 = String            #17           //  B
  #11 = Fieldref          #4.#43        //  Test.B:LTest;
  #12 = String            #18           //  C
  #13 = Fieldref          #4.#44        //  Test.C:LTest;
  #14 = Class             #45           //  java/lang/Enum
  #15 = Utf8              A
  #16 = Utf8              LTest;
  #17 = Utf8              B
  #18 = Utf8              C
  #19 = Utf8              $VALUES
  #20 = Utf8              [LTest;
  #21 = Utf8              values
  #22 = Utf8              ()[LTest;
  #23 = Utf8              Code
  #24 = Utf8              LineNumberTable
  #25 = Utf8              valueOf
  #26 = Utf8              (Ljava/lang/String;)LTest;
  #27 = Utf8              
  #28 = Utf8              (Ljava/lang/String;I)V
  #29 = Utf8              Signature
  #30 = Utf8              ()V
  #31 = Utf8              
  #32 = Utf8              Ljava/lang/Enum;
  #33 = Utf8              SourceFile
  #34 = Utf8              Test.java
  #35 = NameAndType       #19:#20       //  $VALUES:[LTest;
  #36 = Class             #20           //  "[LTest;"
  #37 = NameAndType       #46:#47       //  clone:()Ljava/lang/Object;
  #38 = Utf8              Test
  #39 = NameAndType       #25:#48       //  valueOf:(Ljava/lang/Class;Ljava/la
ng/String;)Ljava/lang/Enum;
  #40 = NameAndType       #27:#28       //  "":(Ljava/lang/String;I)V
  #41 = NameAndType       #27:#28       //  "":(Ljava/lang/String;I)V
  #42 = NameAndType       #15:#16       //  A:LTest;
  #43 = NameAndType       #17:#16       //  B:LTest;
  #44 = NameAndType       #18:#16       //  C:LTest;
  #45 = Utf8              java/lang/Enum
  #46 = Utf8              clone
  #47 = Utf8              ()Ljava/lang/Object;
  #48 = Utf8              (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;

{
  public static final Test A;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Test B;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Test C;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static Test[] values();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
     stack=1, locals=0, args_size=0
        0: getstatic    #1                 // Field $VALUES:[LTest;
        3: invokevirtual #2                 // Method "[LTest;".clone:()Ljava/
lang/Object;
        6: checkcast    #3                 // class "[LTest;"
        9: areturn
     LineNumberTable:
       line 1: 0

  public static Test valueOf(java.lang.String);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
     stack=2, locals=1, args_size=1
        0: ldc_w        #4                 // class Test
        3: aload_0
        4: invokestatic #5                 // Method java/lang/Enum.valueOf:(
Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
        7: checkcast    #4                 // class Test
       10: areturn
     LineNumberTable:
       line 1: 0

  static {};
    flags: ACC_STATIC
    Code:
     stack=4, locals=0, args_size=0
        0: new          #4                 // class Test
        3: dup
        4: ldc          #7                 // String A
        6: iconst_0
        7: invokespecial #8                 // Method "":(Ljava/lang/Str
ing;I)V
       10: putstatic    #9                 // Field A:LTest;
       13: new          #4                 // class Test
       16: dup
       17: ldc          #10                // String B
       19: iconst_1
       20: invokespecial #8                 // Method "":(Ljava/lang/Str
ing;I)V
       23: putstatic    #11                // Field B:LTest;
       26: new          #4                 // class Test
       29: dup
       30: ldc          #12                // String C
       32: iconst_2
       33: invokespecial #8                 // Method "":(Ljava/lang/Str
ing;I)V
       36: putstatic    #13                // Field C:LTest;
       39: iconst_3
       40: anewarray    #4                 // class Test
       43: dup
       44: iconst_0
       45: getstatic    #9                 // Field A:LTest;
       48: aastore
       49: dup
       50: iconst_1
       51: getstatic    #11                // Field B:LTest;
       54: aastore
       55: dup
       56: iconst_2
       57: getstatic    #13                // Field C:LTest;
       60: aastore
       61: putstatic    #1                 // Field $VALUES:[LTest;
       64: return
     LineNumberTable:
       line 2: 0
       line 1: 39
}

结论如下:
1、java.lang.Enum<ElementType>,而编译后是public final class Test extends java.lang.Enum<Test>推不出Test是ElementType的子类这个结果。如果想导出这个结果,应该是Enum<? extends ElementType>。

这里Test和ElementType一点关系都没有。而恰恰相反,Enum的声明是:Enum<? extends Enum<E>>,即后面这个泛型里必须是Enum子类。所以合理的推理是这样的:Test是Enum的子类,ElementType也是Enum的子类。

2、为什么ElementType会有values()和valueOf()方法,这不是刚好,是因为ElementType本身也是Enum的子类,编译时自动添加了这些方法。

3、任何类也不可能是ElementType的子类,因为任何enum编译后都是final修饰的,除非它的某个枚举量有class body,而ElementType没有。final修饰的类不可继承。

一句话总结:编译器自动生成。

本文含有隐藏内容,请 开通VIP 后查看