Enums and Records
Prerequisites: Classes and objects
Enums
An enum is a type with a fixed set of values:
enum Planet {
MERCURY(3.303e+23, 2.4397e6),
EARTH(5.976e+24, 6.37814e6),
JUPITER(1.9e+27, 7.1492e7)
final double mass, radius
Planet(double mass, double radius) {
this.mass = mass
this.radius = radius
}
double surfaceGravity() { 6.67300E-11 * mass / (radius * radius) }
}
Planet.values().each {
printf('%-8s %6.2f m/s²%n', it, it.surfaceGravity())
}
println Planet.valueOf('EARTH').ordinal()
println Planet.EARTH.next()
println (Planet.MERCURY..Planet.JUPITER)
MERCURY 3.70 m/s²
EARTH 9.80 m/s²
JUPITER 24.81 m/s²
1
JUPITER
MERCURY..JUPITER
Enums can have fields, constructors, and methods. Groovy adds next()
and previous(), so enum values also work in ranges.
Enums in switch and maps
enum Level { DEBUG, INFO, WARN, ERROR }
def color(Level level) {
switch (level) {
case Level.DEBUG -> 'grey'
case [Level.INFO, Level.WARN] -> 'yellow'
case Level.ERROR -> 'red'
}
}
println color(Level.WARN)
println Level.values().findAll { it >= Level.WARN }
def counts = new EnumMap<Level, Integer>(Level)
counts[Level.ERROR] = 2
counts[Level.INFO] = 10
println counts
Level lvl = 'DEBUG' // Groovy converts a String to an enum
println lvl.class.simpleName
yellow
[WARN, ERROR]
[INFO:10, ERROR:2]
Level
Records
Groovy 4 added records — compact, immutable data classes, like Java's:
import groovy.transform.RecordOptions
@RecordOptions(copyWith = true)
record Point(int x, int y) {
Point {
if (x < 0 || y < 0) throw new IllegalArgumentException('negative coordinate')
}
double distanceTo(Point o) { Math.hypot(x - o.x, y - o.y) }
}
def a = new Point(0, 0)
def b = new Point(3, 4)
println b
println b.x() + ' ' + b.y
println a.distanceTo(b)
println b == new Point(3, 4)
def (x, y) = b // records support destructuring
println "x=$x y=$y"
println b.copyWith(y: 10)
Point[x=3, y=4]
3 4
5.0
true
x=3 y=4
Point[x=3, y=10]
- A record's fields are final; accessors are
x()(and Groovy also allows.x). - The compact constructor (
Point { ... }) validates arguments. - Groovy adds destructuring, named-argument construction
(
new Point(x: 1, y: 2)), and — when enabled with@RecordOptions(copyWith = true)—copyWithfor changed copies.
Sealed types
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
double area(Shape s) {
switch (s) {
case Circle -> Math.PI * s.r() ** 2
case Square -> s.side() ** 2
}
}
println area(new Square(3))
printf('%.3f%n', area(new Circle(1)))
9.0
3.142
sealed ... permits limits which classes may implement a type, which
makes "one of these cases" data models explicit.