Introduction: The Problem with Regular Classes for Fixed Sets of Values
Consider designing a Season class that should only represent a finite set of values (spring, summer, autumn, winter) and be read-only. A regular class fails because it allows unlimited instances and mutable data through setters.
Example: Inadequate regular class
public class SeasonManager {
public static void main(String[] args) {
Season spring = new Season("Spring", "Warm");
Season summer = new Season("Summer", "Hot");
// Someone could create an invalid season
Season other = new Season("Other", "Invalid");
}
}
class Season {
private String name;
private String description;
public Season(String name, String description) {
this.name = name;
this.description = description;
}
// Getters and setters allow mutation
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
Custom Enum Implementation (Pre-Java 5 Style)
Before Java introduced the enum keyword, developers could simulate enums by making the constructor private and exposing static final instances. This ansures a fixed set of read-only objects.
Example: Custom enum class
public class EnumDemo {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.AUTUMN);
}
}
class Season {
private final String name;
private final String description;
public static final Season SPRING = new Season("Spring", "Warm");
public static final Season SUMMER = new Season("Summer", "Hot");
public static final Season AUTUMN = new Season("Autumn", "Cool");
public static final Season WINTER = new Season("Winter", "Cold");
private Season(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() { return name; }
public String getDescription() { return description; }
@Override
public String toString() {
return "Season{name='" + name + "', description='" + description + "'}";
}
}
Using the enum Keyword
Java provides the enum keyword, which simplifies the above pattern and adds useful methods.
Basic Usage
public class EnumDemo {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.WINTER);
}
}
enum Season {
SPRING("Spring", "Warm"), // Calls constructor with arguments
SUMMER("Summer", "Hot"),
AUTUMN("Autumn", "Cool"),
WINTER("Winter", "Cold");
private final String name;
private final String description;
private Season(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() { return name; }
public String getDescription() { return description; }
@Override
public String toString() {
return "Season{name='" + name + "', description='" + description + "'}";
}
}
Key Points
- An
enumimplicitly extendsjava.lang.Enumand isfinal. Decompiling shows:final class Season extends java.lang.Enum<Season> { ... } - Enum constants are shorthand for
public static finalinstances. The arguments in parentheses correspond to constructor parameters. - If the enum has a no-arg constructor, parentheses and arguments can be omitted.
- Enum constants must be declared first, separated by commas, and terminated with a semicolon.
- Enums cannot extend other classes (since they extend
Enum), but they can implement interfaces. - Enums can have fields, methods, and constructors (which are implicitly private).
Methods Provided by java.lang.Enum
| Method | Description |
|---|---|
toString() |
Returns the constant name (unless overridden). |
name() |
Returns the constant name (cannot be overridden). |
ordinal() |
Returns the zero-based position of the constant. |
values() |
Returns an array of all enum constants. |
valueOf(String) |
Returns the enum constant with the specified name (throws IllegalArgumentException if not found). |
compareTo(Enum) |
Compares by ordinal (returns this.ordinal - other.ordinal). |
Example of using these methods:
public class EnumMethodsDemo {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
System.out.println("toString(): " + autumn); // If overridden: custom string
System.out.println("name(): " + autumn.name()); // "AUTUMN"
System.out.println("ordinal(): " + autumn.ordinal()); // 2
Season[] all = Season.values();
for (Season s : all) {
System.out.println(s);
}
Season spring = Season.valueOf("SPRING");
System.out.println(spring);
System.out.println("AUTUMN.compareTo(SPRING): " + autumn.compareTo(spring)); // 2
}
}
Example with No-Arg Constructor
enum NoArgSeason {
SPRING, SUMMER, AUTUMN, WINTER; // No parentheses needed
}
public class TestNoArgs {
public static void main(String[] args) {
System.out.println(NoArgSeason.SPRING); // SPRING (uses default toString from Enum)
}
}
Note: The default toString() inherited from Enum returns the constant name. If not overridden, the output will be the constant name.
Summary
- Enums are ideal for representing a fixed set of constants.
- They are type-safe and more concise than the pre-Java 5 pattern.
- Enums can have fields, methods, and constructors, and they implement interfaces.
- Use the built-in methods (
values(),valueOf(),ordinal(), etc.) for common operations.