Generics, Annotations and Type Coercion
22.1 Generics
In dynamic mode, generics are mostly documentation (erased and not checked). With @CompileStatic/@TypeChecked, they're enforced.
class Box<T> {
T value
def <R> Box<R> map(Closure<R> f) { new Box<R>(value: f(value)) }
}
def b = new Box<Integer>(value: 5)
println b.map { it * 2 }.value
List<String> names = ['a', 'b']
Map<String, List<Integer>> scores = [ana: [90, 85]]
def <T extends Comparable<T>> T maxOf(List<T> items) { items.max() }22.2 Annotations
import java.lang.annotation.*
@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.METHOD])
@interface Audit { String value() default "default" }
class Service {
@Audit("save") void save() {}
}
println Service.getMethod("save").getAnnotation(Audit).value()
// Closure annotation parameters (Groovy-specific)
@interface Precondition { Class value() }
// Meta-annotations (annotation collectors): @AnnotationCollector
import groovy.transform.*
@AnnotationCollector([ToString, EqualsAndHashCode, Immutable])
@interface Alias {}22.3 Type Coercion with as
Implicit closure coercion (SAM): methods accepting a functional interface accept closures directly: executor.submit { work() }, list.sort { a, b -> a <=> b }.
println "42" as Integer, 3.99 as int, [1, 2, 2] as Set, [3, 1] as int[]
println 'a' as char, 65 as char, "true" as Boolean, [1, 2] as String
def r = { println "run" } as Runnable // closure → interface
def cmp = { a, b -> a <=> b } as Comparator
def iface = [hasNext: { false }, next: { null }] as Iterator // map → interface
class Temp {
double c
Object asType(Class t) { t == Fahrenheit ? new Fahrenheit(f: c * 9 / 5 + 32) : super.asType(t) }
}
class Fahrenheit { double f }
println (new Temp(c: 100) as Fahrenheit).f