user3453226
user3453226

Reputation:

How are enums internally represented in Java?

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 code? I want actual Java code.

public enum Ordinals {
    FIRST("st"),
    SECOND("nd"),
    THIRD("rd");
    private String notation;
    private Ordinals(String notation) {
        this.notation = notation;
    }
    public String getNotation() {
        return notation;
    }
}

Upvotes: 24

Views: 10520

Answers (5)

riccardo.cardin
riccardo.cardin

Reputation: 8353

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.

Upvotes: 4

Adam Michalik
Adam Michalik

Reputation: 9945

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).

Upvotes: 31

RealSkeptic
RealSkeptic

Reputation: 34618

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.

Upvotes: 1

Maroun
Maroun

Reputation: 95958

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 {};
}

Upvotes: 3

Kayaman
Kayaman

Reputation: 73548

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.

Upvotes: 2

Related Questions