Java course menu
Java Records and Enums: Data Classes and Fixed Sets of Constants
Learn Java records (Java 16+): compact immutable data classes with auto-generated constructors, accessors, equals, hashCode and toString, compact constructors for validation; and enums: constants, values(), valueOf, switch, fields, constructors and methods.
On this page
Two special kinds of class save a lot of code: records, for classes that simply hold data, and enums, for a fixed set of named values.
Records: data classes in one line#
A plain data class needs a constructor, getters, equals, hashCode and toString, which is dozens of lines of boilerplate. A record (Java 16+) generates all of them:
record Point(int x, int y) {}
public class Main {
public static void main(String[] args) {
Point p = new Point(3, 4);
Point q = new Point(3, 4);
System.out.println(p);
System.out.println(p.x() + " " + p.y());
System.out.println(p.equals(q) + " " + (p == q));
System.out.println(p.hashCode() == q.hashCode());
}
}Point[x=3, y=4]
3 4
true false
trueFrom record Point(int x, int y), Java generates:
private finalfieldsxandy;- a constructor taking
(int x, int y); - accessor methods
x()andy()(nogetprefix); equalsandhashCodethat compare the values;- a readable
toString.
Records are immutable: there are no setters, and the fields are final.
Validation with a compact constructor#
To check arguments, add a compact constructor, which has no parameter list and runs before the fields are assigned:
record Percentage(double value) {
Percentage {
if (value < 0 || value > 100) {
throw new IllegalArgumentException("must be 0-100: " + value);
}
}
double asFraction() { // records can have methods
return value / 100;
}
}
public class Main {
public static void main(String[] args) {
Percentage p = new Percentage(45);
System.out.println(p + " = " + p.asFraction());
try {
new Percentage(140);
} catch (IllegalArgumentException e) {
System.out.println("Rejected: " + e.getMessage());
}
}
}Percentage[value=45.0] = 0.45
Rejected: must be 0-100: 140.0Records in collections#
Because equals and hashCode are value-based, records work perfectly in sets and as map keys:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
record Student(String name, int marks) {}
public class Main {
public static void main(String[] args) {
List<Student> students = List.of(
new Student("Asha", 92), new Student("Ravi", 85), new Student("Asha", 92));
Set<Student> unique = new HashSet<>(students);
System.out.println(unique.size());
int total = 0;
for (Student s : students) total += s.marks();
System.out.println(total);
}
}2
269Use records for values that are defined by their data: points, money amounts, coordinates, API responses, and results with several parts.
Records cannot extend other classes (they implicitly extend java.lang.Record), but they can implement interfaces.
Enums: a fixed set of named values#
An enum defines a type with a fixed list of constants, such as days of the week or order statuses:
enum OrderStatus { PENDING, SHIPPED, DELIVERED, CANCELLED }
public class Main {
public static void main(String[] args) {
OrderStatus status = OrderStatus.SHIPPED;
if (status == OrderStatus.SHIPPED) {
System.out.println("On its way!");
}
System.out.println(status + " " + status.ordinal());
for (OrderStatus s : OrderStatus.values()) {
System.out.print(s + " ");
}
System.out.println();
System.out.println(OrderStatus.valueOf("DELIVERED"));
}
}On its way!
SHIPPED 1
PENDING SHIPPED DELIVERED CANCELLED
DELIVERED- Enum constants are written in
UPPER_CASE. - Compare enum values with
==(each constant exists exactly once). values()returns all constants;valueOf("NAME")converts text (throwsIllegalArgumentExceptionfor unknown names);ordinal()gives the position.
Using OrderStatus.SHIPPED instead of a magic number like 1 or a string like "shipped" makes code self-explanatory, and the compiler rejects invalid values and typos.
Enums with fields and methods#
Enums are full classes: constants can carry data, and the enum can have methods:
enum Planet {
MERCURY(3.303e23, 2.4397e6),
EARTH(5.976e24, 6.37814e6),
JUPITER(1.9e27, 7.1492e7);
private final double mass; // kg
private final double radius; // m
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
double surfaceGravity() {
return 6.67300e-11 * mass / (radius * radius);
}
}
public class Main {
public static void main(String[] args) {
for (Planet p : Planet.values()) {
System.out.printf("%-8s %.2f m/s2%n", p, p.surfaceGravity());
}
}
}MERCURY 3.70 m/s2
EARTH 9.80 m/s2
JUPITER 24.81 m/s2Enums and switch#
A switch expression over an enum that covers every constant needs no default, and the compiler warns you if a new constant is not handled:
enum Level { BEGINNER, INTERMEDIATE, ADVANCED }
public class Main {
static int hoursPerWeek(Level level) {
return switch (level) {
case BEGINNER -> 3;
case INTERMEDIATE -> 5;
case ADVANCED -> 8;
};
}
public static void main(String[] args) {
System.out.println(hoursPerWeek(Level.INTERMEDIATE));
}
}5EnumMap and EnumSet are specialized, very fast collections keyed by enum constants.
Common mistakes#
Practice#
Practice exercise
Define enum Size { SMALL, MEDIUM, LARGE } with a price for each (100, 150, 200), and record Order(String drink, Size size, int quantity) with a method total(). Print an order of 3 MEDIUM coffees and its total.
Show solution
enum Size {
SMALL(100), MEDIUM(150), LARGE(200);
private final int price;
Size(int price) {
this.price = price;
}
int price() {
return price;
}
}
record Order(String drink, Size size, int quantity) {
int total() {
return size.price() * quantity;
}
}
public class Cafe {
public static void main(String[] args) {
Order order = new Order("coffee", Size.MEDIUM, 3);
System.out.println(order);
System.out.println("Total: " + order.total());
}
}Order[drink=coffee, size=MEDIUM, quantity=3]
Total: 450Key takeaway#
Finished this lesson?