Enum is a very powerful tool in Java. Its declaration defines a class type, different from other languages. Learn how to use it quickly with this guide.
Enumerations are present in several programming languages, but in Java they have special characteristics. In this article we will see in a practical way how enumerations are defined in this language and everything that is necessary to use them correctly.
1 — What are enumerations for
Enumerations are used when we need to define a fixed set of constants. For example, we can create an enumeration with the months of the year (January, February, March…) or with status codes, such as success, waiting, failure.
2 — What are enumerations in Java
It is very important to understand that, unlike other languages, Java implements enumerations as class types, that is, they define a data type. And because enumerations represent a fixed set of values, an object of an enumeration can only have the values declared in its list of constants (months of the year, status codes, etc.).
3 — Creating an enumeration
An enumeration is created using the enum keyword. This declaration defines a class, called an enum type.
//Enumeration of a traffic light
enum TrafficLight {
RED, YELLOW, GREEN;
}
There are a few important things to note:
- The RED, YELLOW, and GREEN identifiers are called enumeration constants ;
- Each identifier is implicitly declared as a final static public field of TrafficLight ;
- Enumeration constants are called self-typed , which means that their type is the same as the enumeration that contains them, that is, RED, YELLOW and GREEN are constants of type TrafficLight .
You can declare a variable of a previously defined enumeration type just as you would any other type. For example, this is how a variable of the TrafficLight enumeration type called tl is declared .
class EnumDemo {
TrafficLight tl;
}
It is not possible to instantiate an enum type using new . Since tl is of type TrafficLight , the only values that can be assigned to it are those defined by the enumeration, and we obtain this value using Java’s static member access syntax — remember that enumeration constants are static public fields:
class EnumDemo {
TrafficLight tl;
tl = TrafficLight.YELLOW;
}
4 — Comparing enumerations
Since it is impossible to explicitly instantiate an enum type, there can only be one instance of each enumeration constant.
Therefore, we can use the relational operator == instead of the equals method to compare two object references of the same enum type. It is also possible to use the equals method but, for simplicity, give preference to the first option.
class EnumDemo {
TrafficLight tl = TrafficLight.YELLOW;
if (tl == TrafficLight.GREEN) // …
if (tl.equals(TrafficLight.RED)) // …
}
5 — Displaying an enumeration constants
Displaying an enumeration constant, for example using println(), results in its name as defined in the enumeration.
class EnumDemo {
System.out.println(TrafficLight.RED);
}
RED
6 — Enumerations in switch statements
You can control a switch statement using the value of an enumeration:
class EnumDemo {
TrafficLight tl = TrafficLight.YELLOW;
switch (tl) {
case RED:
// ...
case YELLOW:
// ...
case GREEN:
// ...
}
}
Two important observations:
- The case statements must use constants of the same enum type as the switch expression ;
- In case statements , enumeration names must be used without specifying their enumeration type (GREEN instead of TrafficLight.GREEN), because it is already implicitly specified in the switch expression , and including it will cause a compilation error.
7 — Uppercase or lowercase?
Java has no stylistic requirements, but many programmers capitalize enumeration constants, as we have done in our examples so far. This preference is due to the fact that enumerations often replace variables of type final , which by tradition are also usually written with capital letters.
8 — Enumerations vs final variables
Many programmers think that enumerations replace final variables, but both should be used in different situations:
Enumerations : They are recommended when we need to define a list of pre-defined items represented by an identifier.
Final variables : They are used to define a constant value, such as the size of an array or the maximum value that an Integer encapsulator variable can assume (Integer.MAX_VALUE).
9 — Defining constructors, methods and instance variables
As each enumeration constant is an object of its enumeration type (TrafficLight.GREEN is an object of type TrafficLight), we can have constructors, methods and instance variables in an enumeration:
- The constructor is called once for each constant when it is created during class initialization;
- Like any other class, an enum type can have two or more overloaded constructors;
- Every enum constructor without an access modifier is private . Attempting to use the public and protected modifiers will cause a compilation error. Therefore, in practice it is only possible to define private constructors on an enum type;
- Each constant has its own copy of any instance variable defined by the enumeration and can call any of its methods.
The next example illustrates the use of a constructor, an instance variable, and a method:
enum TrafficLight {
RED(60), YELLOW(5), GREEN(120);
private final int time;
TrafficLight(int t) {
time = t;
}
int getTime() {
return time;
}
}
class EnumDemo {
public static void main(String[] args) {
System.out.println("The " +
TrafficLight.GREEN + " light time is " +
TrafficLight.GREEN.getTime() + "seconds.");
}
}
The GREEN light time is 120 seconds.
This version of TrafficLight has three new features:
- A time instance variable that contains the time in seconds that the light is active. (Note that we declare time as final because, conceptually, an enumeration constant must represent a fixed value, although it is not mandatory to use this modifier);
- A constructor that takes as an argument the light’s active time;
- The getTime() method , which returns the time value .
Note that constructor arguments are enclosed in parentheses after each constant. These values are passed to the TrafficLight parameter t, whose value is then assigned to the time instance variable .
Since each enumeration constant is an object and therefore has its own copy of time, we can get the time of a specified signal by calling getTime().
10 — values() and valueOf() methods
Every enumeration has two implicit methods, which are added automatically by the compiler when it creates an enum. This is the general form of these methods:
public static E[] values();
public static E valueOf(String name);
- The values() method returns an array containing all enumeration constants in the order in which they were declared; it is commonly used to iterate over the values of an enum type;
- The valueOf() method returns the enumeration constant whose value matches the string passed as an argument or throws an IllegalArgumentException if the enum type has no constant with the specified name.
The following program demonstrates both methods:
enum TrafficLight {
RED, YELLOW, GREEN;
}
public class EnumDemo {
public static void main(String[] args) {
for (TrafficLight tl : TrafficLight.values()) {
System.out.println(tl + " light");
}
System.out.println();
TrafficLight tl = TrafficLight.valueOf("GREEN");
System.out.println("tl contains " + tl);
}
}
RED light
YELLOW ligth
GREEN light
tl contains GREEN
11 — Enum type restrictions
There are two important restrictions on enumerations:
- An enum type cannot be a superclass, that is, it is an implicitly final class (it cannot be extended by another class);
- An enumeration cannot inherit another class.
The second restriction arises from the fact that every enum type implicitly inherits the java.lang.Enum class, and Java does not allow multiple inheritance.
12 — Adding body to an enumeration constant
An enumeration constant can also have a body. This body implicitly defines an anonymous class declaration that extends the containing enum type . The class body follows the rules of anonymous classes, such as not being able to define any explicit constructors. It is also possible to have instance methods in the body but, as we will see in the next example, in order for them to be called outside the enum type it needs to override some method accessible within the enum type.
enum Operation {
PLUS {
@Override
double evaluate(double x, double y) {
return x + y;
}
},
MINUS {
@Override
double evaluate(double x, double y) {
return x - y;
}
},
TIMES {
@Override
double evaluate(double x, double y) {
return x * y;
}
},
DIVIDED_BY {
@Override
double evaluate(double x, double y) {
return x / y;
}
};
// Each constant performs an arithmetic operation
abstract double evaluate(double x, double y);
}
class EnumDemo {
public static void main(String[] args) {
double x = 2.0;
double y = 1.0;
for (Operation op : Operation.values())
System.out.println(x + " " + op + " " + y + " = " + op.evaluate(x, y));
}
}
2.0 PLUS 1.0 = 3.0
2.0 MINUS 1.0 = 1.0
2.0 TIMES 1.0 = 2.0
2.0 DIVIDED_BY 1.0 = 2.0
In this example we have an enumeration that defines the basic arithmetic operations. Each constant has its version of the abstract method evaluate , which performs the operation represented by the constant.