What is enumeration

Enumeration, a new data type in JDK1.5, is a special class that is often used to represent a set of constants, such as a year of the year, 12 months of the year, Monday through Sunday, the error code returned by the service, the method of clearing payments, and so on. Enumerations are defined using the enum keyword.

Use of enumerations

Before we use enumerations, let’s explore why we use enumerations at all.

Now there is a business scenario of settlement payment, alipay and wechat payment in two ways, 1 means Alipay, 2 means wechat payment, also need to obtain the corresponding English name according to the code (1 or 2), if there is no enumeration, we will write it like this.

Public class PayTypeUtil {private static final int ALI_PAY = 1; Private static final int WECHAT_PAY = 2; Public String getPayName(int code) {if (ALI_PAY == code) {return "ALI_PAY "; } if (WECHAT_PAY == code) { return "Wechat_Pay"; } return null; }}Copy the code

If at this time, the product manager said to add a Unionpay payment, it would be necessary to add more if judgment, resulting in as many as there are payment methods, there will be as many as if, very ugly.

If you use enumerations, it becomes very elegant. First look at the code:

Public enum PayTypeEnum {/** pay */ ALI_PAY(1, "ALI_PAY"), /** WECHAT_PAY */ WECHAT_PAY(2, "WECHAT_PAY"); private int code; private String describe; PayTypeEnum(int code, String describe) { this.code = code; this.describe = describe; } public PayTypeEnum find(int code) {for (PayTypeEnum PayTypeEnum: values()) { if (payTypeEnum.getCode() == code) { return payTypeEnum; } } return null; } //getter, setter methods}Copy the code

When we need to extend, we only need to define one more instance, and the other code does not need to move, such as adding one more UnionPay payment.

/ * * alipay * / ALI_PAY (1, "ALI_PAY"), / * * WeChat pay * / WECHAT_PAY (2, "WECHAT_PAY"), /** unionPay */ UNION_PAY(3,"UNION_PAY");Copy the code

In general, in practical projects, the most common writing method is this, mainly simple and clear, easy to expand.

The second common use is in combination with switch-case, where I define an enumeration for all seasons.

Public enum Season {// SPRING, // SUMMER, // AUTUMN, // WINTER; }Copy the code

Then use it with switch.

public static void main(String[] args) throws Exception{ doSomething(Season.SPRING); } private static void doSomething(Season Season){switch (Season){case SPRING: system.out. break; Case SUMMER: system.out.println (" 连 天 lotus leaf infinite blue, 最 天 lotus flower other red "); break; Case AUTUMN: system.out.println (" stop the car to love fenglin late, red in February flowers "); break; Case WINTER: system.out.println (" Plum blossom fragrance from bitter cold, bao Jianfeng from honed out "); break; Default: system.out.println (" Starting up in a dying illness, laughing and asking where the guest came from "); }}Copy the code

Maybe a lot of people think that the use of int, String with switch is enough, why support enumeration, such a design is not a lot of redundant, in fact, it is not.

Might as well, in turn, would like to, if use 1 to 4 represents the four seasons, receives the parameter type is int, without prompting, we only know a few type int is hard to guess the scope of the need to digital string, too, if it’s not enumerated it is hard to see what needs to be passed in parameter, you this is the most critical.

If you use enumerations, the problem is solved. When you call doSomething(), you see the enumeration and know which parameters are passed in because they are already defined in the enumeration class. This is beneficial for project transition, as well as for readability of code.

This restriction not only limits the caller, but also limits the parameters passed in to be defined enumerations without worrying about program errors caused by incorrect parameters passed in.

So when enumerated classes are used properly, the maintainability of a project can be greatly improved.

Enumeration of its own methods

PayTypeEnum = PayTypeEnum = PayTypeEnum = PayTypeEnum = PayTypeEnum

The valueOf () method

This is a static method that gets the enumeration class by passing in a string (the name of the enumeration). If the name passed does not exist, an error is reported.

public static void main(String[] args) throws Exception{
    System.out.println(PayTypeEnum.valueOf("ALI_PAY"));
    System.out.println(PayTypeEnum.valueOf("HUAWEI_PAY"));
}
Copy the code

Values () method

Returns an array containing all enumeration data in an enumeration class.

public static void main(String[] args) throws Exception { PayTypeEnum[] payTypeEnums = PayTypeEnum.values(); for (PayTypeEnum payTypeEnum : payTypeEnums) { System.out.println("code: " + payTypeEnum.getCode() + ",describe: " + payTypeEnum.getDescribe()); }}Copy the code

The ordinal () method

By default, the enumeration class provides a default order for the defined enumeration, and the ordinal() method returns the order of the enumeration.

public static void main(String[] args) throws Exception {
    PayTypeEnum[] payTypeEnums = PayTypeEnum.values();
    for (PayTypeEnum payTypeEnum : payTypeEnums) {
        System.out.println("ordinal: " + payTypeEnum.ordinal() + ", Enum: " + payTypeEnum);
    }
}
/**
ordinal: 0, Enum: ALI_PAY
ordinal: 1, Enum: WECHAT_PAY
ordinal: 2, Enum: UNION_PAY
*/
Copy the code

The name(), toString() methods

Returns the name used to define the enumeration.

public static void main(String[] args) throws Exception { for (Season season : Season.values()) { System.out.println(season.name()); } for (Season season : Season.values()) { System.out.println(season.toString()); }}Copy the code

The output is the same:

SPRING
SUMMER
AUTUMN
WINTER
Copy the code

Why is that? Because the underlying code is the same, name is returned.

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { public final String name() { return name; } public String toString() { return name; }}Copy the code

The difference is that the toString() method is not final and can be overridden, while the name() method cannot.

CompareTo () method

Because enumerations implement the Comparable interface, we must override the compareTo() method to compare the order of enumerations, namely ordinal.

public final int compareTo(E o) { Enum<? > other = (Enum<? >)o; Enum<E> self = this; if (self.getClass() ! = other.getClass() && // optimization self.getDeclaringClass() ! = other.getDeclaringClass()) throw new ClassCastException(); return self.ordinal - other.ordinal; }Copy the code

Because we implement the Comparable interface, we can use it to sort things like this:

Public static void main(String[] args) throws Exception {// Season[] seasons = new Season[]{Season. Season.AUTUMN, Season.SPRING, Season.SUMMER}; Arrays.sort(seasons); for (Season season : seasons) { System.out.println(season); }}Copy the code

Output results, sorted by default:

SPRING
SUMMER
AUTUMN
WINTER
Copy the code

The principle of

Take an enumeration Season as an example to examine the underlying layer of enumeration. On the surface, an enumeration is simple:

Public enum Season {// SPRING, // SUMMER, // AUTUMN, // WINTER; }Copy the code

The compiler actually does a lot of work at compile time. We decompiled the season.class file using javap -v, and you can see a lot of detail.

First we see that enumerations are classes that inherit from the abstract class Enum.

Season extends java.lang.Enum<Season>
Copy the code

Second, the enumeration is initialized with a static block of code.

static {}; descriptor: ()V flags: ACC_STATIC Code: stack=4, locals=0, args_size=0 0: new #4 // class io/github/yehongzhi/user/redisLock/Season 3: dup 4: ldc #7 // String SPRING 6: iconst_0 7: invokespecial #8 // Method "<init>":(Ljava/lang/String; I)V 10: putstatic #9 // Field SPRING:Lio/github/yehongzhi/user/redisLock/Season; 13: new #4 // class io/github/yehongzhi/user/redisLock/Season 16: dup 17: ldc #10 // String SUMMER 19: iconst_1 20: invokespecial #8 // Method "<init>":(Ljava/lang/String; I)V 23: putstatic #11 // Field SUMMER:Lio/github/yehongzhi/user/redisLock/Season; 26: new #4 // class io/github/yehongzhi/user/redisLock/Season 29: dup 30: ldc #12 // String AUTUMN 32: iconst_2 33: invokespecial #8 // Method "<init>":(Ljava/lang/String; I)V 36: putstatic #13 // Field AUTUMN:Lio/github/yehongzhi/user/redisLock/Season; 39: new #4 // class io/github/yehongzhi/user/redisLock/Season 42: dup 43: ldc #14 // String WINTER 45: iconst_3 46: invokespecial #8 // Method "<init>":(Ljava/lang/String; I)V 49: putstatic #15 // Field WINTER:Lio/github/yehongzhi/user/redisLock/Season; 52: iconst_4 53: anewarray #4 // class io/github/yehongzhi/user/redisLock/Season 56: dup 57: iconst_0 58: getstatic #9 // Field SPRING:Lio/github/yehongzhi/user/redisLock/Season; 61: aastore 62: dup 63: iconst_1 64: getstatic #11 // Field SUMMER:Lio/github/yehongzhi/user/redisLock/Season; 67: aastore 68: dup 69: iconst_2 70: getstatic #13 // Field AUTUMN:Lio/github/yehongzhi/user/redisLock/Season; 73: aastore 74: dup 75: iconst_3 76: getstatic #15 // Field WINTER:Lio/github/yehongzhi/user/redisLock/Season; 79: aastore 80: putstatic #1 // Field $VALUES:[Lio/github/yehongzhi/user/redisLock/Season; 83: returnCopy the code

This static code block generates the VALUES of the four static constant fields, as well as the $VALUES field, which holds the enumeration constants defined by the enumeration class. Equivalent to executing the following code:

Season SPRING = new Season1();
Season SUMMER = new Season2();
Season AUTUMN = new Season3();
Season WINTER = new Season4();
Season[] $VALUES = new Season[4];
$VALUES[0] = SPRING;
$VALUES[1] = SUMMER;
$VALUES[2] = AUTUMN;
$VALUES[3] = WINTER;
Copy the code

The third method, values(), is a static method that returns an array of the enumerated classes. The underlying implementation works like this.

public static io.github.yehongzhi.user.redisLock.Season[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[Lio/github/yehongzhi/user/redisLock/Season;
       3: invokevirtual #2                  // Method "[Lio/github/yehongzhi/user/redisLock/Season;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[Lio/github/yehongzhi/user/redisLock/Season;"
       9: areturn
Copy the code

Instead, clone the $VALUES array initialized by the static block and force a Season[] return. Equivalent to this:

public static Season[] values(){
    return (Season[])$VALUES.clone();
}
Copy the code

So, on the surface, you just add an enum keyword to define the enumeration, but once the underlying class is recognized as an enumeration, the compiler does special treatment for the enumeration class by initializing the enumeration with a static block of code, which always provides values() whenever it is an enumeration.

We also know from decompilation that all Enum superclasses are abstract Enum classes, so Enum has member variables that implement interfaces, and subclasses also have them.

So any enumeration will have two fields, name, ordinal, and let’s look at the constructor for Enum.

/**
* Sole constructor.  Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*/
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}
Copy the code

A unique constructor that a programmer cannot call. It is used by the compiler in response to enumeration type declarations. It concludes that the creation of enumeration instances is also done by the compiler.

Enumeration implements singletons

Many people say that enumerated classes are the best way to implement singletons, because singletons of enumerated classes are thread-safe and the only singletons pattern implementation that can’t be broken. That is, you cannot create an instance by reflection, ensuring that there is only one instance in the entire application, a very hardcore singleton.

Public class SingletonObj {// Enumeration of internal classes private enum SingletonEnum {INSTANCE; private SingletonObj singletonObj; SingletonObj SingletonEnum() {singletonObj = new singletonObj (); } private SingletonObj getSingletonObj() { return singletonObj; }} public static SingletonObj getInstance() {public static SingletonObj getInstance() { Return to return SingletonEnum. INSTANCE. GetSingletonObj (); } public static void main(String[] args) {SingletonObj a = singletonobj. getInstance(); SingletonObj b = SingletonObj.getInstance(); System.out.println(a == b); //true } }Copy the code

Suppose someone wanted to create an enumeration class through reflection. Let’s take the Season enumeration as an example.

public static void main(String[] args) throws Exception { Constructor<Season> constructor = Season.class.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); Season Season = constructor.newinstance ("NEW_SPRING", 4); // Create an enumeration of Season Season = constructor.newinstance ("NEW_SPRING", 4); System.out.println(season); }Copy the code

An error is then reported because reflection calls are not allowed on enumeration constructors.

If you look at the source code, you can see that there is an if judgment for enumerations.

public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException { if (! override) { if (! Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<? > caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } // If ((clazz.getModiFIERS () & Modifier.ENUM)! Throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; }Copy the code

conclusion

Enumerations may seem like a small amount of knowledge, but there’s a lot to learn when we dig deeper. The first point is that using enumerations to define constants is easier to extend, and the code is more readable and maintainable. Then the second point is to understand the methods that come with enumerations. The third point, through decompilation, explores what the compiler does for enumerations at compile time. Finally, an example of enumeration implementing the singleton pattern.

.

More Java learning materials, can join my ten years of Java learning garden, notes [Ling Hu], there are more Java basic technology information, technology learning exchange, all kinds of technology training camp learning.

Link: blog.csdn.net/yehongzhi19…