closure.resolveStrategy = Closure.DELEGATE_FIRST descriptiom - unix1998/technical_notes GitHub Wiki

In Groovy, closures have a property called resolveStrategy which determines how methods and properties within the closure are resolved. The resolveStrategy affects the delegation behavior of closures, i.e., how the closure resolves method calls and property accesses. There are several strategies available:

  1. OWNER_FIRST (default): The closure first attempts to resolve methods and properties in its owner (the enclosing class or closure). If not found, it then looks in its delegate.
  2. DELEGATE_FIRST: The closure first attempts to resolve methods and properties in its delegate. If not found, it then looks in its owner.
  3. OWNER_ONLY: The closure only resolves methods and properties in its owner, ignoring the delegate.
  4. DELEGATE_ONLY: The closure only resolves methods and properties in its delegate, ignoring the owner.
  5. TO_SELF: The closure resolves methods and properties within itself (typically used with nested closures).

Explanation of Closure.DELEGATE_FIRST

When you set closure.resolveStrategy = Closure.DELEGATE_FIRST, it means that the closure will first attempt to resolve any method calls or property accesses on the delegate object. If the method or property is not found on the delegate, it will then fall back to the owner.

This is particularly useful in DSLs where you want the closure to apply settings to a specific object (the delegate), even though the closure itself might be defined in another context (the owner).

Example

Here’s a simple example to illustrate the different resolve strategies:

class Example {
    String name = 'Owner'

    def run() {
        def closure = {
            println "Closure name: ${name}"
        }

        // Setting delegate
        def delegate = new Delegate()
        closure.delegate = delegate

        // Using different resolve strategies
        println 'OWNER_FIRST (default)'
        closure.resolveStrategy = Closure.OWNER_FIRST
        closure() // Output: Closure name: Owner

        println 'DELEGATE_FIRST'
        closure.resolveStrategy = Closure.DELEGATE_FIRST
        closure() // Output: Closure name: Delegate

        println 'OWNER_ONLY'
        closure.resolveStrategy = Closure.OWNER_ONLY
        closure() // Output: Closure name: Owner

        println 'DELEGATE_ONLY'
        closure.resolveStrategy = Closure.DELEGATE_ONLY
        closure() // Output: Closure name: Delegate

        println 'TO_SELF'
        closure.resolveStrategy = Closure.TO_SELF
        closure() // Output: Closure name: Owner (since name is not found in the closure itself)
    }
}

class Delegate {
    String name = 'Delegate'
}

new Example().run()

Using in Your Context

In your Groovy DSL example, you want the closure to apply settings to the WebServer instance. Therefore, using Closure.DELEGATE_FIRST ensures that properties and methods defined on WebServer are resolved first within the closure:

import org.example.WebServer

def call(Closure closure) {
    def server = new WebServer()
    closure.delegate = server
    closure.resolveStrategy = Closure.DELEGATE_FIRST
    closure()
    server.start()
}

In this setup, when you call methods like name, port, and route within the closure, Groovy will first try to resolve these methods on the WebServer instance (delegate) before looking at the enclosing context (owner). This makes it easier to build DSLs where the closure configuration should apply to a specific object.

Summary of Strategies

  • OWNER_FIRST (default): Looks in the owner first, then delegate.
  • DELEGATE_FIRST: Looks in the delegate first, then owner.
  • OWNER_ONLY: Only looks in the owner.
  • DELEGATE_ONLY: Only looks in the delegate.
  • TO_SELF: Looks within the closure itself.

These strategies allow you to control how Groovy closures resolve methods and properties, which is particularly useful in building flexible and intuitive DSLs.