How Java enumeration types are implemented

Source: Internet
Author: User
Tags class operator

Java supports enumerations starting with JDK1.5, that is, Java does not support enumeration at first, just like generics, which is a new feature added by JDK1.5. Usually a feature that is not provided at the beginning of the language development is added later, and there is a problem with backward compatibility. Like Java in 1.5 introduced many features, for backwards compatibility, the compiler will help us write the source code to do a lot of things, such as why the generic erase type, why the generation of bridging method, foreach iteration, automatic boxing/unpacking, etc., there is a term called "syntax Sugar", and the compiler's special processing called " To solve the grammatical sugars. " So like enumeration is also introduced in the JDK1.5, and how to achieve it?

Java added the Java.lang.Enum abstract class in 1.5, which is the base class for all enum types. Provides some basic properties and basic methods. At the same time, support is provided for using enumerations as set and map, i.e. Java.util.EnumSet and Java.util.EnumMap.

How to define enum types

For example, subtraction operation, we can define the following enumeration:

Package com.mikan;/** * @author Mikan * @date 2015-08-29 12:06 */public enum Operator {    ADD,    SUBTRACT,    MULTIPL Y,    DIVIDE}
The above enumeration defines four enumeration constants, and in the enumeration you can define a common method, an abstract method, as follows:

Package com.mikan;/** * @author Mikan * @date 2015-08-29 12:06 */public enum Operator {    ADD {        @Override        public  int calculate (int a, int b) {            return a + b;        }    },    SUBTRACT {        @Override public        int calculate (int A, int b) {            return        A-C;}    },    MULTIPLY {        @Override public        int calculate (int a, int b) {            R Eturn A * b;        }    ,    DIVIDE {        @Override public        int calculate (int a, int b) {            if (b = = 0) {
   
    throw new IllegalArgumentException ("divisor must not being 0");            }            return a/b;        }    };    public abstract int Calculate (int a, int b);}
   
As you can see from above, we can basically define enumerations as if we were defining classes. We can also define attributes, construct methods, and so on:

Package com.mikan;/** * @author Mikan * @date 2015-08-29 12:06 */public enum Operator {    ADD ("+") {        @Override        public int calculate (int A, int. b) {            return a + b;        }    },    SUBTRACT ("-") {        @Override public        int Calc Ulate (int A, int b) {return A-a,        }    ,    MULTIPLY  ("*") {        @Override public        int Calculate (int a, int b) {            return a * b;        }    },    DIVIDE ("/") {        @Override public        int Calculate ( int a, int b) {            if (b = = 0) {                throw new illegalargumentexception ("divisor must not is 0");            }            return a/b;        }    };    Operator (String Operator) {        this.operator = Operator;    }    Private String operator;    public abstract int Calculate (int a, int b);    Public String Getoperator () {        return operator;    }}

Implementation principle analysis Now that you can use enumerations as you would with normal classes, what exactly does the compiler do for us? The most effective way to know the secret of this is to look at the generated bytecode. Here's a look at what the bytecode generated by the enumeration above defines.

First, take a look at the basic information for anti-compilation:

Localhost:mikan mikan$ javap operator.classcompiled from "Operator.java" public abstract class Com.mikan.Operator Extends java.lang.enum<com.mikan.operator> {public  static final Com.mikan.Operator ADD;  public static final Com.mikan.Operator SUBTRACT;  public static final Com.mikan.Operator MULTIPLY;  public static final Com.mikan.Operator DIVIDE;  public static com.mikan.operator[] values ();  public static Com.mikan.Operator valueOf (java.lang.String);  public abstract int calculate (int, int);  Public java.lang.String getoperator ();  Com.mikan.Operator (java.lang.String, int, java.lang.String, com.mikan.operator$1);  static {};}
As you can see, an enumeration has been compiled by the compiler into an abstract class that inherits Java.lang.Enum, and the enumeration constant defined in the enumeration becomes the corresponding public static final property, and its type is the type of the abstract class, and the name is the name of the enumeration constant. , and we can see the. class files of four inner classes under the same path of Operator.class Com/mikan/operator$1.class, Com/mikan/operator$2.class, com/mikan/ Operator$3.class, Com/mikan/operator$4.class, which means that these four named fields are implemented using an inner class, and two methods are added, values () and valueof (String) The construction method we defined originally had only one parameter, but it became three parameters, and a static code block was generated. Take a closer look at these specific elements.

Look at the following detailed anti-compilation information:

Localhost:mikan mikan$ javap-c-v operator.classclassfile/users/mikan/documents/workspace/project/algorithm/target /classes/com/mikan/operator.class last modified 2015-8-29; Size 1720 bytes MD5 checksum 478439554cb827fec3c36cf51c8d36da Compiled from ' Operator.java ' public abstract class Com.mik An. Operator extends java.lang.enum<com.mikan.operator> Signature: #67//Ljava/lang/enum<l  com/mikan/operator;>; SourceFile: "Operator.java" innerclasses:static #24; Class com/mikan/operator$4 static #19; Class Com/mikan/operator$3 static #14; Class Com/mikan/operator$2 static #9; Class Com/mikan/operator$1 minor version:0 major version:51 Flags:acc_public, Acc_super, Acc_abstract, ACC_ENUMCon    Stant Pool://Omit constant pool information {public static final com.mikan.Operator ADD;    Flags:acc_public, Acc_static, acc_final, Acc_enum public STATIC FINAL com.mikan.Operator SUBTRACT; Flags:acc_public, Acc_static, Acc_final, Acc_enum public static final Com.mikan.Operator MULTIPLY;    Flags:acc_public, Acc_static, acc_final, Acc_enum public STATIC FINAL com.mikan.Operator DIVIDE;    Flags:acc_public, Acc_static, acc_final, Acc_enum public STATIC com.mikan.operator[] values ();  Flags:acc_public, Acc_static code:stack=1, locals=0, args_size=0 0:getstatic #2//         Field $VALUES: [Lcom/mikan/operator; 3:invokevirtual #3//Method "[Lcom/mikan/operator;].         Clone: () Ljava/lang/object;         6:checkcast #4//Class "[Lcom/mikan/operator;"]    9:areturn linenumbertable:line 7:0 public static Com.mikan.Operator valueOf (java.lang.String);  Flags:acc_public, Acc_static code:stack=2, Locals=1, args_size=1 0:ldc_w #5// Class Com/mikan/operator 3:aload_0 4:invokestatic #6//Method java/lang/enum.valueof :(Ljava/lang/class; Ljava/lang/string;) Ljava/lang/enum;       7:checkcast #5//class Com/mikan/operator 10:areturn linenumbertable:line 7:0 Localvariabletable:start Length Slot name Signature 0 0 Name ljava/lang/st  Ring    public abstract int calculate (int, int);    Flags:acc_public, Acc_abstract public java.lang.String getoperator ();                  Flags:acc_public code:stack=1, Locals=1, args_size=1 0:aload_0 1:getfield #8         Field operator:ljava/lang/string;               4:areturn linenumbertable:line 46:0 localvariabletable:start Length Slot Name Signature  0 5 0 This lcom/mikan/operator;    Com.mikan.Operator (java.lang.String, int, java.lang.String, com.mikan.operator$1);          Flags:acc_synthetic code:stack=4, locals=5, args_size=5 0:aload_0 1:aload_1 2:iload_2 3:aload_3 4:invokespeciAl #1//Method "<init>":(ljava/lang/string;iljava/lang/string;) V 7:return linenumber     Table:line 7:0 localvariabletable:start Length Slot Name Signature 0 8               0 this lcom/mikan/operator;               0 8 1 x0 ljava/lang/string;               0 8 2 x1 I 0 8 3 x2 ljava/lang/string;  0 8 4 X3 lcom/mikan/operator$1;    static {}; Flags:acc_static code:stack=5, locals=0, args_size=0 0:new #9//Class com/m ikan/operator$1 3:dup 4:LDC #10//String ADD 6:iconst_0 7:l DC #11//String + 9:invokespecial #12//Method com/mikan/operator$1. " <init> ":(ljava/lang/string;iljava/lang/string;) V 12:putstatic #13//Field Add:lcom/mika N/operaTor                 15:new #14//class com/mikan/operator$2 18:dup 19:LDC #15 String SUBTRACT 21:iconst_1 22:ldc #16//String-24:invokespe cial #17//Method com/mikan/operator$2. " <init> ":(ljava/lang/string;iljava/lang/string;) V 27:putstatic #18//Field subtract:lcom        /mikan/operator;                 30:new #19//class com/mikan/operator$3 33:dup 34:LDC #20 String MULTIPLY 36:iconst_2 37:ldc #21//String * 39:invokespe cial #22//Method com/mikan/operator$3. " <init> ":(ljava/lang/string;iljava/lang/string;) V 42:putstatic #23//Field multiply:lcom        /mikan/operator; 45:new #24//class com/mikan/operator$4 48:dup 49:LDC #25//String DIVIDE 51:iconst_3 52:ldc #26//STR Ing/54:invokespecial #27//Method com/mikan/operator$4. " <init> ":(ljava/lang/string;iljava/lang/string;) V 57:putstatic #28//Field divide:lcom/m        Ikan/operator;        60:iconst_4 61:anewarray #5//class Com/mikan/operator 64:dup 65:iconst_0        66:getstatic #13//Field add:lcom/mikan/operator; 69:aastore 70:dup 71:iconst_1 72:getstatic #18//Field subtract:lcom/mikan/o        Perator; 75:aastore 76:dup 77:iconst_2 78:getstatic #23//Field multiply:lcom/mikan/o        Perator; 81:aastore 82:dup 83:iconst_3 84:getstatic #28//Field Divide:lcom/mikan/ope        Rator; 87:aastore 88:putstatic     #2//Field $VALUES: [Lcom/mikan/operator; 91:return linenumbertable:line 9:0 line 15:15 line 21:30 line 27:45 Line 7: 60}localhost:mikan mikan$
The following is an analysis of the various parts of the bytecode, where:

  Innerclasses:       static #24;//class com/mikan/operator$4       static #19;//class com/mikan/operator$3       Static # 14; Class com/mikan/operator$2       static #9;//class com/mikan/operator$1
From there, you can see that it has 4 inner classes, which are parsed after the details of the four inner classes.

Static code block:

  static {};                  Flags:acc_static code:stack=5, locals=0, args_size=0//Create a Operator$1 Inner class object 0:new #9         Class Com/mikan/operator$1 3:dup//The next three instructions are to push three parameters to the top of the stack, and then call Operator$1 's compiler-generated <init> method 4:LDC #10//String ADD 6:iconst_0 7:ldc #11//STR ing +//calls <init> methods 9:invokespecial #12//Method com/mikan/operator$1. " <init> ":(ljava/lang/string;iljava/lang/string;) v//set the value of the Add property to the newly created object 12:putstatic #13//                 Field add:lcom/mikan/operator;//The next step is to initialize the other three properties subtract, MULTIPLY, DIVIDE, and this will not repeat 15:new #14 Class com/mikan/operator$2 18:dup 19:LDC #15//String SUBTRACT 21  : iconst_1 22:ldc #16//string-24:invokespecial #17//Method Com/mikan/operator$2. " <iNit> ":(ljava/lang/string;iljava/lang/string;) V 27:putstatic #18//Field Subtract:lcom/mika        N/operator;                 30:new #19//class com/mikan/operator$3 33:dup 34:LDC #20 String MULTIPLY 36:iconst_2 37:ldc #21//String * 39:invokespe cial #22//Method com/mikan/operator$3. " <init> ":(ljava/lang/string;iljava/lang/string;) V 42:putstatic #23//Field multiply:lcom        /mikan/operator;                 45:new #24//class com/mikan/operator$4 48:dup 49:LDC #25 String DIVIDE 51:iconst_3 52:ldc #26//String/54:invokespeci Al #27//Method com/mikan/operator$4. " <init> ":(ljava/lang/string;iljava/lang/string;) V 57:putstatic #28//Field DIVIde:lcom/mikan/operator;//The following is a new array of type Operator with a length of 4 and sets the value of each element in the array to the value of the above four properties 60:iconst_4 61:anewarray                 #5//Class Com/mikan/operator 64:dup 65:iconst_0 66:getstatic #13        Field Add:lcom/mikan/operator; 69:aastore 70:dup 71:iconst_1 72:getstatic #18//Field subtract:lcom/mikan/o        Perator; 75:aastore 76:dup 77:iconst_2 78:getstatic #23//Field multiply:lcom/mikan/o        Perator; 81:aastore 82:dup 83:iconst_3 84:getstatic #28//Field Divide:lcom/mikan/ope        Rator;        87:aastore//below is the Set property $values value for the array just created 88:putstatic #2//Field $VALUES: [Lcom/mikan/operator; 91:return
In fact, this static block of code generated by the compiler does the following: Set the values of the generated four public static constant fields separately, and the compiler also generates a static field $values, which holds all enumerated constants defined by the enumeration type. Equivalent to the following code:

Operator ADD = new Operator1 () Operator SUBTRACT = new Operator1 (); Operator MULTIPLY = new Operator1 (); Operator DIVIDE = n EW Operator1 (); operator[] $VALUES = new operator[4]; $VALUES [0] = ADD; $VALUES [0] = SUBTRACT; $VALUES [0] = MULTIPLY; $VALUES [ 0] = DIVIDE;
the values method added by the compiler:

  public static com.mikan.operator[] values ();    Flags:acc_public, acc_static    Code:      stack=1, locals=0, args_size=0         0:getstatic     #2                  //Field $ Values:[lcom/mikan/operator;         3:invokevirtual #3                  //Method "[Lcom/mikan/operator;]. Clone: () Ljava/lang/object;         6:checkcast     #4                  //Class "[Lcom/mikan/operator;"]         9:areturn
This method is a public static method, so we can call the method directly (Operator.values ()), return an array of this enumeration value, in addition, the implementation of this method is to clone the value of the $values field initialized in the static code block, and turn the type strong to operator[] type to return. It is equivalent to the following code:

public static com.mikan.operator[] values () {return (operator[]) $VALUES. Clone ();
valueof Method added by the compiler:

  public static Com.mikan.Operator valueOf (java.lang.String);    Flags:acc_public, acc_static    Code:      stack=2, Locals=1, args_size=1         0:ldc_w         #5                  //Class Com/mikan /operator         3:aload_0         4:invokestatic  #6                  //Method java/lang/enum.valueof: (Ljava/lang/class; ljava/lang/string;) Ljava/lang/enum;         7:checkcast     #5                  //class Com/mikan/operator        10:areturn
This method is a public static method, so we can call the method directly (Operator.valueof ()), return the enumeration constant represented by the parameter string, and the implementation of this method is to call the ValueOf method of the parent class enum and convert the type to Operator. It is equivalent to the following code:

public static Com.mikan.Operator valueOf (String name) {return (Operator) enum.valueof (operator.class, name);}
generated inner class

Here's a look at the generated inner class operator$1:

Localhost:mikan mikan$ javap operator\$1.classcompiled from "Operator.java" final class Com.mikan.operator$1 extends Com.mikan.Operator {  com.mikan.operator$1 (java.lang.String, int, java.lang.String);  public int calculate (int, int);} Localhost:mikan mikan$
As you can see, implementing an inner class is inherited from operator, which is

    ADD {        @Override public        int calculate (int a, int b) {            return a + b;        }    },
This means that each enumerated constant that we define eventually generates an inner class like the one above.

Why did the construction method add two methods?

There is a problem, the construction method we clearly only defined a parameter, why the construction method is generated three parameters?

As we can see from the Enum class, two properties are defined for each enumeration, and name and ordinal,name represent the names of enumerated constants we define, such as add, subtract, and so on, while ordinal is a sequential number that assigns an integer value in the order defined. Starting from 0. When enumeration constants are initialized, the corresponding values are automatically set for the initialization of the two fields, so two parameters are added to the constructor method. That

  Com.mikan.operator$1 (string name, int ordinal, string Operator);
The inner classes generated by the other three enumeration constants are basically similar, and are not repeated here.

We can see from the code of the enum class that the defined name and ordinal properties are final, and most of the methods are final, especially the three methods of clone, ReadObject, WriteObject, These three methods and enumerations are initialized with a static block of code, which guarantees that the enumeration type is immutable, cannot be cloned, and cannot be copied by serialization and deserialization, which guarantees that an enumeration constant is just an instance, that is, a singleton, so the effective Enumeration is recommended in Java for example.

Summarize

Enumerations are essentially implemented by ordinary classes, except that the compiler handles them for us. Each enumeration type inherits from Java.lang.Enum, and the values and valueof methods are added automatically. Each enumeration constant is a static constant field that is implemented with an inner class that inherits the enumeration class. All enumeration constants are initialized by static blocks of code, which are initialized during class loading. In addition, the three methods of clone, ReadObject, and writeobject are defined as final, and the corresponding exception is thrown by the implementation. This ensures that each enumeration type and enumeration constants are immutable. You can take advantage of these two attributes of enumerations to implement thread-safe singleton.

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

How Java enumeration types are implemented

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.