How are enums internally represented in Java?

后端 未结 5 1655
南旧
南旧 2020-12-15 18:54

Java enums are classes. They are compiled as classes.

How will the following example be compiled? What is its \"class version\"? What is the exact class cod

相关标签:
5条回答
  • 2020-12-15 19:00

    You can think about enums as a class with typed constants. You can think of your code as:

    public enum Ordinals {
        public static final FIRST = "st";
        public static final SECOND = ..;
        // ...
    
        private String notation;
    
        private Ordinals(String notation) {
            this.notation = notation;
        }
        public String getNotation() {
            return notation;
        }
    }
    

    The javap command might be useful for you, try this:

    javap Example.class:

    public final class Example extends java.lang.Enum<Example> {
      public static final Example FIRST;
      public static final Example SECOND;
      public static final Example THIRD;
      public static Example[] values();
      public static Example valueOf(java.lang.String);
      public java.lang.String getNotation();
      static {};
    }
    
    0 讨论(0)
  • 2020-12-15 19:08

    The output of javap -private for this is:

    public final class Ordinals extends java.lang.Enum<Ordinals> {
      public static final Ordinals FIRST;
      public static final Ordinals SECOND;
      public static final Ordinals THIRD;
      private java.lang.String notation;
      private static final Ordinals[] $VALUES;
      public static Ordinals[] values();
      public static Ordinals valueOf(java.lang.String);
      private Ordinals(java.lang.String);
      public java.lang.String getNotation();
      static {};
    }
    

    As you see, the compiler added a few things.

    There is a synthetic Array $VALUES, which is going to be copied out whenever you call the values() method.

    The values() method itself is added to the class, to conform to the requirement of every enum that there should be a static values() method returning an array of all the enum constants. Why is it added by the compiler rather than inherited from Enum()? Because it is static.

    In the same manner, valueOf has been added. It is implemented by calling the static Enum.valueOf.

    One thing that you do not see in this listing is that the constructors have two additional, unseen parameters, which are passed when the enum constants are set up. They are the constant's name and its ordinal number. They are passed up to the super() constructor from Enum and used for the methods name() and ordinal() which are inherited from it.

    0 讨论(0)
  • 2020-12-15 19:11

    Each enum class is compiled as a class being a subclass of java.lang.Enum. Each enum constant becomes a static final constant within that class. Then, an array $VALUES is created with all of the enum constants, in order of declaration.

    You can disassemble the code using the command javap -p -c Ordinals (on the compiled .class file) to find out the details.

    Compiled from "Ordinals.java"
    public final class Ordinals extends java.lang.Enum<Ordinals> {
      public static final Ordinals FIRST;
    
      public static final Ordinals SECOND;
    
      public static final Ordinals THIRD;
    
      private java.lang.String notation; // your custom field
    
      private static final Ordinals[] $VALUES; // all enum constants
    
      public static Ordinals[] values(); // every enum class has this static method
        Code:
           0: getstatic     #1                  // Field $VALUES:[LOrdinals;
           3: invokevirtual #2                  // Method "[LOrdinals;".clone:()Ljava/lang/Object;
           6: checkcast     #3                  // class "[LOrdinals;"
           9: areturn       
    
      public static Ordinals valueOf(java.lang.String); // every enum class has this static method
        Code:
           0: ldc_w         #4                  // class Ordinals
           3: aload_0       
           4: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
           7: checkcast     #4                  // class Ordinals
          10: areturn       
    
      private Ordinals(java.lang.String);
        Code:
           0: aload_0       
           1: aload_1       
           2: iload_2       
           3: invokespecial #6                  // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
           6: aload_0       
           7: aload_3       
           8: putfield      #7                  // Field notation:Ljava/lang/String;
          11: return        
    
      public java.lang.String getNotation();
        Code:
           0: aload_0       
           1: getfield      #7                  // Field notation:Ljava/lang/String;
           4: areturn       
    
      static {}; // fills the $VALUES array and initializes the static fields corresponding to the enum constants
        Code:
           0: new           #4                  // class Ordinals
           3: dup           
           4: ldc           #8                  // String FIRST
           6: iconst_0      
           7: ldc           #9                  // String st
           9: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
          12: putstatic     #11                 // Field FIRST:LOrdinals;
          15: new           #4                  // class Ordinals
          18: dup           
          19: ldc           #12                 // String SECOND
          21: iconst_1      
          22: ldc           #13                 // String nd
          24: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
          27: putstatic     #14                 // Field SECOND:LOrdinals;
          30: new           #4                  // class Ordinals
          33: dup           
          34: ldc           #15                 // String THIRD
          36: iconst_2      
          37: ldc           #16                 // String rd
          39: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
          42: putstatic     #17                 // Field THIRD:LOrdinals;
          45: iconst_3      
          46: anewarray     #4                  // class Ordinals
          49: dup           
          50: iconst_0      
          51: getstatic     #11                 // Field FIRST:LOrdinals;
          54: aastore       
          55: dup           
          56: iconst_1      
          57: getstatic     #14                 // Field SECOND:LOrdinals;
          60: aastore       
          61: dup           
          62: iconst_2      
          63: getstatic     #17                 // Field THIRD:LOrdinals;
          66: aastore       
          67: putstatic     #1                  // Field $VALUES:[LOrdinals;
          70: return        
    }
    

    That would translate back to Java as

    public final class Ordinals extends java.lang.Enum<Ordinals> {
      public static final Ordinals FIRST;
    
      public static final Ordinals SECOND;
    
      public static final Ordinals THIRD;
    
      private String notation;
    
      private static final Ordinals[] $VALUES;
    
      public static Ordinals[] values() {
          return $VALUES.clone();
      }
    
      public static Ordinals valueOf(String name) {
          return (Ordinals) Enum.valueOf(Ordinals.class, name);
      }
    
      private Ordinals(String name, int ordinal, String notation) {
          super(name, ordinal);
          this.notation = notation
      }
    
      static {
          FIRST = new Ordinals("FIRST", 0, "st");
          SECOND = new Ordinals("SECOND", 1, "nd");
          THIRD = new Ordinals("THIRD", 2, "rd");
          Ordinals[] $VALUES = new Ordinals[3];
          $VALUES[0] = FIRST;
          $VALUES[1] = SECOND;
          $VALUES[2] = THIRD;
          Ordinals.$VALUES = $VALUES;
      }
    }
    

    Class version is totally unrelated to that - it depends on the version of the Java compiler that you used (or on the explicit setting on the compiler to force it to compile for an older Java version).

    0 讨论(0)
  • 2020-12-15 19:15

    Using the javap - p Ordinals.class command you will obtain the following code:

    public final class Ordinals extends java.lang.Enum<Ordinals> {
        public static final Ordinals FIRST;
        public static final Ordinals SECOND;
        public static final Ordinals THIRD;
        private java.lang.String notation;
        private static final Ordinals[] $VALUES;
        public static Ordinals[] values();
        public static Ordinals valueOf(java.lang.String);
        private Ordinals(java.lang.String);
        public java.lang.String getNotation();
        static {};
    }
    

    So, every value of the enum is translated to a public static final field of the class type. If I did understand right, in the static block those field are initialized using the private constructor, such that

    static {
        Ordinals.FIRST = new Ordinals("st");
        Ordinals.SECOND = new Ordinals("nd");
        Ordinals.THIRD = new Ordinals("rd");
        // ...
    }
    

    As stated in the item 3 of the Effective Java book, an enum is treated more or less like a Singleton and this is reflected in the use of the above public final static fields.

    0 讨论(0)
  • 2020-12-15 19:21

    Class version is what tells which Java version a class file is compiled with. You probably weren't interested in that.

    Your enum will be compiled normally, with Ordinals implicitly extending the Enum class, and with 3 class level variables, all instances of the Ordinals class.

    0 讨论(0)
提交回复
热议问题