Enums, Records and Sealed Types

19.1 Enums

enum Color { RED, GREEN, BLUE }

enum Planet {
    MERCURY(3.303e+23, 2.4397e6), EARTH(5.976e+24, 6.37814e6)
    final double mass, radius
    Planet(double mass, double radius) { this.mass = mass; this.radius = radius }
    double surfaceGravity() { 6.67300E-11 * mass / (radius * radius) }
}

enum Op {
    PLUS { int apply(int a, int b) { a + b } },
    TIMES { int apply(int a, int b) { a * b } }
    abstract int apply(int a, int b)
}

println Color.values(), Color.RED.ordinal(), Color.valueOf('GREEN'), 'BLUE' as Color
println Planet.EARTH.surfaceGravity().round(2)
println Op.TIMES.apply(3, 4)
def c = Color.GREEN
switch (c) { case Color.RED: println "stop"; break; case Color.GREEN: println "go"; break }
for (col in Color) println col
println (Color.RED..Color.BLUE)                // enums support ranges

19.2 Records (Groovy 4)

Records are immutable. They compile to native JVM records on JDK 16+ (or emulated records on older JDKs). @RecordOptions customizes them.

record Point(int x, int y) {
    Point {                                    // compact constructor (validation)
        if (x < 0) throw new IllegalArgumentException("x < 0")
    }
    double distance() { Math.sqrt(x * x + y * y) }
}
def p = new Point(3, 4)
println p, p.x(), p.x, p.distance()            // property-style access works too
def (px, py) = p                                // destructuring (Groovy records support getAt)
println p == new Point(3, 4)                    // structural equality
println p.copyWith(y: 10)                       // copyWith (record option)
println p.toMap()

19.3 Sealed Types (Groovy 4)

@Sealed(permittedSubclasses = [...]) is the annotation alternative. Native JVM sealed classes are generated on JDK 17+.

sealed interface Shape permits Circle, Rect {}
final class Circle implements Shape { double r }
final class Rect implements Shape { double w, h }
// non-sealed class Other implements Shape {}  // must be listed in permits

def area(Shape s) {
    switch (s) {
        case Circle -> Math.PI * s.r ** 2
        case Rect -> s.w * s.h
    }
}