Enums in Java are more powerful than in many other languages. They can have constructors, methods, fields, and even implement interfaces.
Basic Enum Syntax
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
Enum with Constructor, Fields and Methods
Simple Example
public enum Planet {
// Enum constants with constructor arguments
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6),
JUPITER(1.9e+27, 7.1492e7),
SATURN(5.688e+26, 6.0268e7),
URANUS(8.686e+25, 2.5559e7),
NEPTUNE(1.024e+26, 2.4746e7);
// Fields
private final double mass; // in kilograms
private final double radius; // in meters
// Constructor (always private by default)
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
// Methods
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
public double getSurfaceGravity() {
final double G = 6.67300E-11; // gravitational constant
return G * mass / (radius * radius);
}
public double getSurfaceWeight(double otherMass) {
return otherMass * getSurfaceGravity();
}
}
Usage Example
public class PlanetTest {
public static void main(String[] args) {
double earthWeight = 175; // in pounds
double mass = earthWeight / Planet.EARTH.getSurfaceGravity();
for (Planet p : Planet.values()) {
System.out.printf("Your weight on %s is %f%n",
p, p.getSurfaceWeight(mass));
}
}
}
Advanced Enum Features
Enum with Multiple Fields and Custom Methods
public enum Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
// Constructor
Operation(String symbol) {
this.symbol = symbol;
}
// Abstract method - each constant must implement
public abstract double apply(double x, double y);
// Regular method
public String getSymbol() {
return symbol;
}
// Static utility method
public static Operation fromSymbol(String symbol) {
for (Operation op : values()) {
if (op.symbol.equals(symbol)) {
return op;
}
}
throw new IllegalArgumentException("Unknown operator: " + symbol);
}
}
Usage
public class Calculator {
public static void main(String[] args) {
double x = 10, y = 5;
for (Operation op : Operation.values()) {
System.out.printf("%f %s %f = %f%n",
x, op.getSymbol(), y, op.apply(x, y));
}
// Using the static method
Operation plus = Operation.fromSymbol("+");
System.out.println("5 + 3 = " + plus.apply(5, 3));
}
}
Enum with State and Behavior
Complex Example with Multiple Constructors
public enum HttpStatus {
// Success responses
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
// Client error responses
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
// Server error responses
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
BAD_GATEWAY(502, "Bad Gateway");
private final int code;
private final String description;
private final Category category;
// Regular constructor
HttpStatus(int code, String description) {
this.code = code;
this.description = description;
this.category = determineCategory(code);
}
// Private helper method
private Category determineCategory(int code) {
if (code >= 200 && code < 300) return Category.SUCCESS;
if (code >= 400 && code < 500) return Category.CLIENT_ERROR;
if (code >= 500 && code < 600) return Category.SERVER_ERROR;
return Category.UNKNOWN;
}
// Methods
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
public Category getCategory() {
return category;
}
public boolean isSuccess() {
return category == Category.SUCCESS;
}
public boolean isError() {
return category == Category.CLIENT_ERROR || category == Category.SERVER_ERROR;
}
// Static lookup method
public static HttpStatus fromCode(int code) {
for (HttpStatus status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("Unknown HTTP status code: " + code);
}
// Nested enum
public enum Category {
SUCCESS, CLIENT_ERROR, SERVER_ERROR, UNKNOWN
}
@Override
public String toString() {
return code + " " + description;
}
}
Usage
public class HttpStatusTest {
public static void main(String[] args) {
HttpStatus status = HttpStatus.NOT_FOUND;
System.out.println("Code: " + status.getCode());
System.out.println("Description: " + status.getDescription());
System.out.println("Category: " + status.getCategory());
System.out.println("Is error: " + status.isError());
System.out.println("Is success: " + status.isSuccess());
System.out.println("ToString: " + status);
// Lookup by code
HttpStatus found = HttpStatus.fromCode(200);
System.out.println("Found: " + found);
}
}
Enum Implementing Interface
// Interface
public interface Cacheable {
String getCacheKey();
boolean isCacheable();
}
// Enum implementing interface
public enum UserRole implements Cacheable {
ADMIN("admin", 1, true),
MODERATOR("moderator", 2, true),
USER("user", 3, true),
GUEST("guest", 4, false);
private final String roleName;
private final int level;
private final boolean cacheable;
UserRole(String roleName, int level, boolean cacheable) {
this.roleName = roleName;
this.level = level;
this.cacheable = cacheable;
}
// Interface method implementations
@Override
public String getCacheKey() {
return "ROLE_" + name();
}
@Override
public boolean isCacheable() {
return cacheable;
}
// Additional methods
public String getRoleName() {
return roleName;
}
public int getLevel() {
return level;
}
public boolean hasPermission(String permission) {
// Logic to check permissions based on role level
return this.level <= getRequiredLevel(permission);
}
private int getRequiredLevel(String permission) {
// Permission level mapping logic
return switch (permission) {
case "DELETE" -> 1;
case "EDIT" -> 2;
case "VIEW" -> 3;
default -> 4;
};
}
}
Enum with Singleton Pattern
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
// Initialize connection
initializeConnection();
}
private void initializeConnection() {
try {
// Simulate connection creation
System.out.println("Creating database connection...");
// this.connection = DriverManager.getConnection(url);
} catch (Exception e) {
throw new RuntimeException("Failed to create database connection", e);
}
}
public Connection getConnection() {
return connection;
}
public void executeQuery(String query) {
System.out.println("Executing: " + query);
// Actual query execution logic
}
public void close() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
System.err.println("Error closing connection: " + e.getMessage());
}
}
}
Singleton Usage
public class SingletonDemo {
public static void main(String[] args) {
// Get the singleton instance
DatabaseConnection db = DatabaseConnection.INSTANCE;
db.executeQuery("SELECT * FROM users");
// All calls to INSTANCE return the same instance
DatabaseConnection sameInstance = DatabaseConnection.INSTANCE;
System.out.println("Same instance? " + (db == sameInstance)); // true
}
}
Best Practices
- Use descriptive names for enum constants
- Keep enum constructors private (they already are by default)
- Make enum fields final when possible for immutability
- Override toString() for better readability
- Use enum for fixed set of constants that won't change frequently
- Consider using enums instead of boolean parameters for better readability
// BAD - boolean parameters are confusing
public void setVisible(boolean visible, boolean modal)
// GOOD - enum parameters are clear
public void setVisible(Visibility visible, Modality modal)
public enum Visibility { VISIBLE, HIDDEN }
public enum Modality { MODAL, MODELESS }
Key Benefits
- Type safety - Compiler prevents invalid values
- Rich functionality - Can have methods, fields, and implement interfaces
- Singleton support - Natural implementation of singleton pattern
- Serialization safety - Built-in serialization support
- Switch statement compatibility - Can be used in switch statements
Enums with constructors and methods provide a powerful way to represent fixed sets of related constants with associated behavior and data.