Closures
12.1 Closure Basics
def greet = { name -> "Hello, $name" }
println greet("Ana")
println greet.call("Bo")
def square = { it * it } // implicit parameter `it`
def noArgs = { -> "no params" } // explicitly zero params
def multi = { a, b = 10 -> a + b } // default parameter
def varargs = { String... args -> args.join(",") }
def typed = { int x, int y -> x * y }
println square(5), multi(1), varargs('a', 'b'), typed(3, 4)
println square.maximumNumberOfParameters, typed.parameterTypes12.2 Closures Capture Their Environment
def counter = 0
def inc = { counter++ }
3.times { inc() }
println counter // 3 (captures by reference)
def makeMultiplier(n) { return { x -> x * n } }
def triple = makeMultiplier(3)
println triple(7) // 2112.3 this, owner, and delegate
The enclosing class instance where the closure is defined
The enclosing object: a class instance or an outer closure
A third-party object used to resolve method calls and properties (defaults to owner). Configurable.
Resolve strategies: OWNER_FIRST (default), DELEGATE_FIRST, OWNER_ONLY, DELEGATE_ONLY, TO_SELF.
Delegation is the foundation of Groovy DSLs and builders (Gradle, Jenkins).
with and tap: cfg.with { host = "x"; port = 1 } (returns the closure's result), new Config().tap { host = "y" } (returns the object).
class Config {
String host; int port
}
def configure = {
host = "localhost" // resolved against the delegate
port = 8080
}
def cfg = new Config()
configure.delegate = cfg
configure.resolveStrategy = Closure.DELEGATE_FIRST
configure()
println "$cfg.host:$cfg.port"12.4 Currying, Composition, Memoization, Trampoline
def add = { a, b -> a + b }
def add5 = add.curry(5) // left curry
def rcurried = { a, b -> a - b }.rcurry(1)
def ncurried = { a, b, c -> a + b + c }.ncurry(1, 10)
println add5(3), rcurried(10)
def plus2 = { it + 2 }
def times3 = { it * 3 }
def composed = plus2 >> times3 // times3(plus2(x))
def reversed = plus2 << times3 // plus2(times3(x))
println composed(1), reversed(1) // 9, 5
def slowFib
slowFib = { n -> n < 2 ? n : slowFib(n - 1) + slowFib(n - 2) }.memoize()
println slowFib(80)
def limited = { it * 2 }.memoizeAtMost(100)
def factorial
factorial = { n, acc = 1G -> n <= 1 ? acc : factorial.trampoline(n - 1, acc * n) }.trampoline()
println factorial(1000).toString().size() // no StackOverflow12.5 Closures as Method Arguments
def repeat(int times, Closure action) { times.times { action(it) } }
repeat(3) { println "Iteration $it" } // trailing closure syntax
def timeIt(Closure c) { def s = System.nanoTime(); c(); println "${(System.nanoTime() - s) / 1e6} ms" }
timeIt { (1..100000).sum() }12.6 Closures vs Lambdas vs Method References
def c = { x -> x * 2 } // Closure
java.util.function.Function<Integer, Integer> f = x -> x * 2 // Java lambda syntax (Groovy 3+)
def ref = String::toUpperCase // method reference
def ptr = "text".&toUpperCase // method pointer
Runnable r = { println "SAM coercion" } // closure → functional interface