Java Enum with Values: Fields, Constructors and Methods
Java enum constants can carry data and behaviour. Fields and private constructors, constant-specific methods, enums implementing interfaces and lookup by code.
- Course: Java study plan
- Module: Enums & records
- Kind: Lesson
- Reading time: 14 min
- Runtime: OpenJDK 18
How do you add fields and a constructor to an enum in Java?
To add fields and a constructor to an enum in Java, list each constant with its arguments — EARTH(5.976e+24, 6.37814e6) — end the list with a semicolon, then declare final fields and a constructor that assigns them. The constructor is implicitly private and runs once per constant when the enum class is initialised; methods then read the fields like in any class.
Lesson
An enum is a class, so its constants can carry data and its type can have methods. That turns a bare list of names into a table of related facts — a planet's mass, a currency's symbol, an operation's implementation — that lives with the type instead of in parallel arrays or switch statements scattered through the code. This lesson covers enum fields and constructors, per-constant behaviour, enums implementing interfaces, and the lookup patterns you will write for every enum that maps to external codes.
Fields and constructors
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
EARTH (5.976e+24, 6.37814e6),
JUPITER(1.9e+27, 7.1492e7);
private final double mass; // kg
private final double radius; // m
Planet(double mass, double radius) { // implicitly private; no access modifier allowed but private
this.mass = mass;
this.radius = radius;
}
public double surfaceGravity() { return 6.67300E-11 * mass / (radius * radius); }
public double mass() { return mass; }
}
Planet.EARTH.surfaceGravity(); // 9.8
Each constant is created by calling the constructor with its arguments, at class initialisation, in declaration order. Rules:
- The constant list comes first and ends with a semicolon when anything follows.
- Constructors are implicitly
private; you cannot call them, only the constant declarations do. - Fields should be
final: an enum with mutable fields is a global variable with several names — legal, and almost always a mistake. - Static fields are not accessible from the constructor (they may not be initialised yet, since constants are created first); compute lookup tables in a static block after the constants exist.
Methods and overriding toString
public enum Level {
LOW("low"), MEDIUM("medium"), HIGH("high");
private final String label;
Level(String label) { this.label = label; }
@Override public String toString() { return label; }
public boolean atLeast(Level other) { return compareTo(other) >= 0; }
}
toString is for display; name() stays the identifier ("LOW") and is final. Business methods using ordinal-based compareTo are fine when declaration order is meaningful and documented.
Per-constant behaviour
When each constant needs a different implementation, two styles:
Constant-specific class bodies — each constant overrides an abstract method:
public enum Operation {
ADD("+") { public int apply(int a, int b) { return a + b; } },
SUB("-") { public int apply(int a, int b) { return a - b; } },
MUL("*") { public int apply(int a, int b) { return a * b; } };
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
public abstract int apply(int a, int b);
public String symbol() { return symbol; }
}
Operation.MUL.apply(6, 7); // 42
Each constant with a body is an anonymous subclass (the enum is then not final, and getClass() differs from getDeclaringClass()). The compiler forces every constant to implement the abstract method — adding DIV without apply is an error.
A functional field — pass the behaviour as a lambda:
public enum Operation {
ADD("+", (a, b) -> a + b),
SUB("-", (a, b) -> a - b),
MUL("*", (a, b) -> a * b);
private final String symbol;
private final IntBinaryOperator op;
Operation(String symbol, IntBinaryOperator op) { this.symbol = symbol; this.op = op; }
public int apply(int a, int b) { return op.applyAsInt(a, b); }
}
Shorter, and the preferred modern form. Either beats a switch (this) inside the method, which must be edited for every new constant and cannot be checked for completeness.
Enums implementing interfaces
public interface Shape { double area(); }
public enum UnitShape implements Shape {
SQUARE { public double area() { return 1; } },
CIRCLE { public double area() { return Math.PI; } };
}
An enum cannot extend a class but can implement any number of interfaces. This lets enums plug into polymorphic code and, for sealed interfaces, serve as fixed-instance variants alongside records.
Lookup by code
Enums frequently correspond to external codes — database values, protocol bytes, user strings. valueOf handles only the exact constant name; write a lookup for everything else:
public enum Status {
ACTIVE("A"), SUSPENDED("S"), CLOSED("C");
private final String code;
Status(String code) { this.code = code; }
public String code() { return code; }
private static final Map<String, Status> BY_CODE = new HashMap<>();
static {
for (Status s : values()) BY_CODE.put(s.code, s); // built once, after the constants exist
}
public static Status fromCode(String code) {
Status s = BY_CODE.get(code);
if (s == null) throw new IllegalArgumentException("unknown status code: " + code);
return s;
}
public static Optional<Status> tryFromCode(String code) { return Optional.ofNullable(BY_CODE.get(code)); }
}
The static block runs after all constants are constructed (the constants are the first static initialisers), so values() is complete. A Map beats a linear scan for more than a handful of constants; Arrays.stream(values()).filter(…).findFirst() is fine for small enums.
Enums and switch, revisited
With behaviour on the enum, most switch (status) blocks elsewhere in the code become status.method() calls — polymorphism instead of dispatch tables. Keep switch for logic that belongs to the caller (a UI deciding colours), and put logic that belongs to the concept on the enum.
Initialisation order pitfalls
enum Bad {
A, B;
private static int count = 0;
Bad() { count++; } // compile error: illegal reference to static field from initializer
}
Constants are constructed before other static fields are initialised, so constructors cannot touch static fields. Count in a static block instead, or use values().length.
EnumSet and EnumMap preview
Collections of enums should be EnumSet (a bit set) and EnumMap (an array indexed by ordinal) — orders of magnitude cheaper than HashSet/HashMap and iterating in declaration order. Next lesson.
Interview angle
- "Can an enum have a constructor?" Yes, implicitly private, run once per constant at class initialisation.
- "How do you give each constant different behaviour?" Abstract method with constant-specific bodies, or a functional-interface field.
- "Why can't an enum constructor read a static field?" Constants initialise first; the static field would still be default.
- "How do you map a database code to an enum?" A static
Map<String, Enum>built in a static block, with afromCodefactory. - "Can an enum implement an interface?" Yes — any number.
Key takeaways
- Constants first, then
finalfields, a private constructor and methods; each constant is built with its arguments. - Per-constant behaviour: an abstract method overridden per constant, or a lambda-typed field — not a
switch (this). - Enums implement interfaces and plug into polymorphic code.
- Lookup by external code through a static map built in a static block; keep
name()for persistence. - Constructors run before other statics — compute tables afterwards.
Common questions
Can an enum have a constructor in Java?
Yes, but it is implicitly private and you never call it yourself: each constant declaration calls it once, with its own arguments, when the enum class is initialised. Using new on an enum type is a compile error.
How do you give each enum constant different behaviour?
Either declare an abstract method in the enum and give every constant a body that overrides it, or pass the behaviour to the constructor as a lambda field, such as an IntBinaryOperator. Both beat a switch (this), which must be edited for every new constant.
How do you look up an enum by a code or value in Java?
Build a static final Map from code to constant in a static block after the constants, looping over values(), and expose a fromCode method that throws IllegalArgumentException, or returns an Optional, for an unknown code. valueOf only matches the exact constant name.
Why can't an enum constructor access a static field?
Enum constants are created first, before the enum's other static fields are initialised, so a static field read in the constructor would still hold its default value. The compiler rejects it with "illegal reference to static field from initializer"; build such tables in a static block instead.
Can an enum implement an interface in Java?
Yes, any number. An enum cannot extend a class, because it already extends Enum, but implementing an interface lets its constants plug into polymorphic code that expects that interface type.
Exercises
Operations as an enum
Declare enum Op with constants ADD("+"), SUB("-"), MUL("*"), DIV("/"), a symbol field, an abstract long apply(long a, long b) implemented per constant (DIV uses integer division and throws ArithmeticException on zero — let it propagate to a catch in main that prints error), and a static fromSymbol(String) lookup built from a static Map.
Read an integer n and n lines <a> <symbol> <b>; print each result, or error for division by zero, or unknown for a symbol that is not an operation.
Example: 3 then 6 * 7, 1 / 0, 2 ^ 3 →
42
error
unknownCoins with values
Declare enum Coin { QUARTER(25), DIME(10), NICKEL(5), PENNY(1) } with a cents field, a constructor and an accessor. Read an integer n and n amounts in cents. For each, make change greedily from the largest coin down (values() is already in that order) and print <amount>: <k> QUARTER, <k> DIME, … listing only coins used, then coins=<total count>.
Example: 2 then 41 30 →
41: 1 QUARTER, 1 DIME, 1 NICKEL, 1 PENNY coins=4
30: 1 QUARTER, 1 NICKEL coins=2In this module: Enums & records
← Enums — a fixed set of named instances · EnumSet and EnumMap →