Enums
Picture a small deck of four cards: a jack, a queen, a king and an ace. The deck was printed at a factory, and you cannot print a fifth card at home. Each card has its own name, and in the box they lie in order.
How do you keep such a card in a program? You could use a string: String card = "king";. But then the compiler lets through both the typo "kign" and the card "joker", which the deck does not have. A number such as int card = 2; is no better: the code does not say what 2 means, and card = 9 compiles too. You need a type that has only the listed values:
public class Main {
public static void main(String[] args) {
Card c = Card.KING;
System.out.println(c); // KING
System.out.println(c.ordinal()); // 2
System.out.println(c == Card.KING); // true
for (Card x : Card.values()) {
System.out.print(x + " "); // JACK QUEEN KING ACE
}
}
}
enum Card { JACK, QUEEN, KING, ACE }Let's go through the program step by step:
enum Card { … }afterMaindeclares the typeCardwith exactly four values. Their names are written in capital letters, separated by commas, like the names of constants.Card c = Card.KING;reaches a value through the type name and a dot.println(c)prints the value's name:KING.c.ordinal()returns the value's position in the declaration, counting from zero:JACKis0,QUEENis1,KINGis2.c == Card.KINGgivestrue.- The for-each loop goes over the array
Card.values()and prints all the cards in order.
What an enum is
An enum is a type whose values are all listed in its declaration. You declare it with the word enum, and its values are called enum constants. Each constant is an object of type Card, and Java creates it exactly once.
What follows from this:
- there are no new values: for
new Card()the compiler answersenum classes may not be instantiated; - a variable of type
Cardcannot be given a string or a number, only one of the four constants ornull; - two variables holding
Card.KINGalways refer to the same object.
Interviewers ask whether enums may be compared with ==. They may: each constant exists as a single instance, and == gives the same answer as equals.
Here the deck picture is not exact. You can print as many decks as you like, and each has its own king. The constant Card.KING is one for the whole program: nobody copies it or prints it again.
name, ordinal and values
Every enum has ready-made methods:
name()returns the constant's name as written in the code:"KING";printlnprints the same name;ordinal()returns the constant's position in the declaration, counting from zero;Card.values()returns an array of all the constants in declaration order.
The position depends only on the order in the declaration: reorder the constants, and the positions change. That is why ordinal() is not used as a permanent code of a value, for example to save it to a file.
enum in a switch
An enum is convenient to handle in a switch. In case labels, the constants are written without the type name. Let's count a card's points:
int points = switch (c) {
case JACK -> 2;
case QUEEN -> 3;
case KING -> 4;
case ACE -> 11;
};
System.out.println(points); // 4As you already know, a switch expression must give a value for any input. Here all four constants are listed, so no default is needed. If you forget one, the compiler answers the switch expression does not cover all possible input values. It will also tell you this when a new card is added to the deck.
A field and a constructor
An enum is the same kind of class, only with objects known in advance. That is why its constants can have fields, a constructor and methods. The points can live right in each card:
public class Main {
public static void main(String[] args) {
System.out.println(Card.ACE.points); // 11
}
}
enum Card {
JACK(2), QUEEN(3), KING(4), ACE(11);
final int points;
Card(int points) {
this.points = points;
}
}Let's go through the declaration:
JACK(2)is a constant with an argument: for it, Java calls the constructorCard(2).- A semicolon follows the last constant, and the field and the constructor come after it.
final int points;gives each card its own number of points, which does not change after the constructor.Card.ACE.pointsgives11.
Only Java itself calls an enum's constructor, once for each constant. Now a new card comes into the deck together with its points, and there is no way to forget them.
Common mistake
A constant was compared with a string:
Card c = Card.KING;
if (c.equals("KING")) { // ✗
System.out.println("king");
}The program prints nothing and reports no error. c is an object of type Card, "KING" is a string, and equals gives false for objects of different types. The compiler would reject c == "KING" with incomparable types: Card and String, but it lets this one through. Compare with the constant: c == Card.KING. If the input is a string, compare the name with it: c.name().equals("KING").
Key points
enumdeclares a type with values listed in advance; you cannot create new ones withnew.- Each constant exists as a single instance, so enums are compared with
==. name()andprintlngive the constant's name,ordinal()its position from zero,values()an array of all the constants.- A switch expression over an enum without
defaultmust list every constant; fields and a constructor give each constant its data.