Star: github.com/yehongzhi/l…
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 {
/ / alipay
private static final int ALI_PAY = 1;
// Wechat Pay
private static final int WECHAT_PAY = 2;
// Get the name of the payment method based on the code
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 {
/** alipay */
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;
}
// Get the payment method according to the code
public PayTypeEnum find(int code) {
for (PayTypeEnum payTypeEnum : values()) {
if (payTypeEnum.getCode() == code) {
returnpayTypeEnum; }}return null;
}
// Getter and 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"),
// Just add one more line of code to complete the extension
/** 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 {
/ / in the spring
SPRING,
/ / summer
SUMMER,
/ / in the autumn
AUTUMN,
/ / winter
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.println("I do not know who cut the thin leaves, February spring breeze like scissors.");
break;
case SUMMER:
System.out.println("Next day lotus leaves infinite blue, reflected in the sun lotus red.");
break;
case AUTUMN:
System.out.println("Parking to sit in love fenglin late, frost leaves red flowers in February.");
break;
case WINTER:
System.out.println("Plum blossom from the bitter cold, the sword from the hone.");
break;
default:
System.out.println("Starting up in his deathbed, laughing and asking where he 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(a) {
return name;
}
public String toString(a) {
returnname; }}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() &&// optimizationself.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 {
// This is an out-of-order enumerated array
Season[] seasons = new Season[]{Season.WINTER, Season.AUTUMN, Season.SPRING, Season.SUMMER};
// Call the sort method to sort by default
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 {
/ / in the spring
SPRING,
/ / summer
SUMMER,
/ / in the autumn
AUTUMN,
/ / winter
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 "
":(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 "
":(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 "
":(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 "
":(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: return
Copy 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 {
// Inner classes use enumerations
private enum SingletonEnum {
INSTANCE;
private SingletonObj singletonObj;
// Initialize singletonObj in the constructor of the enumerated class
SingletonEnum() {
singletonObj = new SingletonObj();
}
private SingletonObj getSingletonObj(a) {
returnsingletonObj; }}// A method to get a singleton provided externally
public static SingletonObj getInstance(a) {
// Get the singleton object, return
return SingletonEnum.INSTANCE.getSingletonObj();
}
/ / test
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);
// Invoke the constructor through reflection to create the enumeration
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); }}// Check whether enumeration is an enumeration. If so, throw an exception
if((clazz.getModifiers() & Modifier.ENUM) ! =0)
// Throw an exception. Enumerations cannot be created by reflection
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.
That’s the end of this article, thank you for reading, I hope you can have a harvest after reading this article!
The article continues to update, wechat search “Java technology enthusiasts”, the first time after paying attention to the technical article push, article classification is collected on github: github.com/yehongzhi, you can always find what you are interested in
Please give me a thumbs-up if you think it is useful. Your thumbs-up is the biggest motivation for my creation
I’m a programmer who tries to be remembered. See you next time!!
Ability is limited, if there is any mistake or improper place, please criticize and correct, study together!