Appearance
Enum Class
In Java, we can define constants through static final
. For example, if we want to define seven constants from Monday to Sunday, they can be represented by seven different int
:
java
public class Weekday {
public static final int SUN = 0;
public static final int MON = 1;
public static final int TUE = 2;
public static final int WED = 3;
public static final int THU = 4;
public static final int FRI = 5;
public static final int SAT = 6;
}
When using constants, you can quote them like this:
java
if (day == Weekday.SAT || day == Weekday.SUN) {
// TODO: work at home
}
Constants can also be defined as string types. For example, define constants for three colors:
java
public class Color {
public static final String RED = "r";
public static final String GREEN = "g";
public static final String BLUE = "b";
}
When using constants, you can quote them like this:
java
String color = ...
if (Color.RED.equals(color)) {
// TODO:
}
Whether it is an int
constant or String
constant, when using these constants to represent a set of enumeration values, there is a serious problem that the compiler cannot check the rationality of each value. For example:
java
if (weekday == 6 || weekday == 7) {
if (tasks == Weekday.MON) {
// TODO:
}
}
The above code will compile and run without error, but there are two problems:
- Note that the constant range defined by
Weekday
is0
~6
, and does not include7
The compiler cannot checkint
values that are not in the enumeration; - The defined constant can still be compared to other variables, but its purpose is not to enumerate day of the week values.
enum
In order for the compiler to automatically check that a certain value is in the enumeration set, and that enumerations for different purposes require different types to be marked and cannot be mixed, we can use enum
to define an enumeration class:
java
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}
enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}
Note that defining an enumeration class is achieved through the keyword enum
, and we only need to list the constant names of the enumeration in sequence.
Compared with constants defined by int
, using enum
to define enumerations has the following benefits:
First of all, enum
constant itself carries type information, that is, Weekday.SUN
type is Weekday
, and the compiler will automatically check for a type error. For example, the following statement will not compile:
java
int day = 1;
if (day == Weekday.SUN) { // Compile error: bad operand types for binary operator '=='
}
Secondly, it is impossible to reference a non-enumerated value because it will not compile.
Finally, enumerations of different types cannot be compared or assigned to each other because the types do not match. For example, you cannot assign a value of the Color
enumeration type to a variable of the Weekday
enumeration type:
java
Weekday x = Weekday.SUN; // ok!
Weekday y = Color.RED; // Compile error: incompatible types
This allows the compiler to automatically detect all possible potential errors at compile time.
enum comparison #
An enumeration class defined using enum
is a reference type. As we mentioned earlier, when comparing reference types, you need to use equals()
method. If you use ==
comparison, it compares whether two reference type variables are the same object. Therefore, when comparing reference types, always use equals()
method, with the exception of enum
types.
This is because each constant of the enum
type has only one unique instance in the JVM, so it can be compared directly with ==
:
java
if (day == Weekday.FRI) { // ok!
}
if (day.equals(Weekday.SUN)) { // ok, but more code!
}
enum type
What is the difference between enumeration classes defined through enum
and other class
?
The answer is there is no difference. The type defined by enum
is class
, but it has the following characteristics:
- The defined
enum
type always inherits fromjava.lang.Enum
and cannot be inherited; - Only instances of
enum
can be defined, but instances ofenum
cannot be created through thenew
operator; - Each instance defined is a unique instance of the reference type;
- You can use
enum
type forswitch
statement.
For example, the Color
enumeration class we defined:
java
public enum Color {
RED, GREEN, BLUE;
}
class
compiled by the compiler probably looks like this:
java
public final class Color extends Enum {
// Each instance is globally unique:
public static final Color RED = new Color();
public static final Color GREEN = new Color();
public static final Color BLUE = new Color();
// private construction method to ensure that the new operator cannot be called externally:
private Color() {}
}
Therefore, there is no difference between the compiled enum
class and the ordinary class
. But we ourselves cannot define enum
like a normal class
, we must use the enum
keyword, which is stipulated by Java grammar.
Because enum
is a class
, the value of each enumeration is class
instance. Therefore, these instances have some methods:
name()
returns the constant name, for example:
java
String s = Weekday.SUN.name(); // "SUN"
ordinal()
returns the order of defined constants, counting from 0, for example:
java
int n = Weekday.MON.ordinal(); // 1
Changing the order in which the enumeration constants are defined will cause the return value of ordinal()
to change. For example:
java
public enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}
and
java
public enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
ordinal
is different. If you write a statement like if(x.ordinal()==1)
in the code, you must ensure that the enumeration order of enum
cannot be changed. New constants must be placed last.
Some children may think that if Weekday
's enumeration constant needs to be converted to int
, wouldn't it be very convenient to use ordinal()
? For example, write like this:
java
String task = Weekday.MON.ordinal() + "/ppt";
saveToFile(task);
However, if the order of the enumeration is accidentally modified, the compiler cannot detect this logical error. To write robust code, don't rely on the return value of ordinal()
. Because enum
itself is class
, we can define a private
constructor and add fields to each enumeration constant:
java
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day.dayValue == 6 || day.dayValue == 0) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}
enum Weekday {
MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);
public final int dayValue;
private Weekday(int dayValue) {
this.dayValue = dayValue;
}
}
In this way, there is no need to worry about changes in order. When adding an enumeration constant, you also need to specify an int
value.
Notice
The fields of the enumeration class can also be of non-final type, that is, they can be modified during runtime, but this is not recommended!
By default, calling toString()
on an enum constant returns the same string as name()
. However, toString()
can be overridden, but name()
cannot. We can add toString()
method to Weekday
:
java
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day.dayValue == 6 || day.dayValue == 0) {
System.out.println("Today is " + day + ". Work at home!");
} else {
System.out.println("Today is " + day + ". Work at office!");
}
}
}
enum Weekday {
MON(1, "Monday"), TUE(2, "Tuesday"), WED(3, "Wednesday"), THU(4, "Thursday"), FRI(5, "Friday"), SAT(6, "Saturday"), SUN(0, "Sunday");
public final int dayValue;
private final String chinese;
private Weekday(int dayValue, String chinese) {
this.dayValue = dayValue;
this.chinese = chinese;
}
@Override
public String toString() {
return this.chinese;
}
}
The purpose of overriding toString()
is to make the output more readable.
Notice
To determine the name of an enumeration constant, always use the name()
method and never call toString()
!
switch
Finally, enumeration classes can be used in switch
statements. Because enumeration classes inherently have type information and a limited number of enumeration constants, they are more suitable for use in switch
statements than int
and String
types:
java
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
switch(day) {
case MON:
case TUE:
case WED:
case THU:
case FRI:
System.out.println("Today is " + day + ". Work at office!");
break;
case SAT:
case SUN:
System.out.println("Today is " + day + ". Work at home!");
break;
default:
throw new RuntimeException("cannot process " + day);
}
}
}
enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
With the addition of default
statement, an error can be automatically reported when an enumeration constant is omitted, so that errors can be discovered in time.
Summary
Java uses enum
to define enumeration types, which is compiled by the compiler as final class Xxx extends Enum { … }
;
Get the string defined by the constant through name()
, be careful not to use toString()
;
Return the order of constant definition through ordinal()
(no real meaning);
Constructors, fields and methods can be written for enum
The constructor of enum
should be declared as private
, and the fields are strongly recommended to be declared as final
;
enum
is suitable for use in switch statements.