{"id":23902935,"url":"https://github.com/mtumilowicz/groovy-closure-owner-delegate-this","last_synced_at":"2025-08-19T04:44:39.632Z","repository":{"id":110876049,"uuid":"157458177","full_name":"mtumilowicz/groovy-closure-owner-delegate-this","owner":"mtumilowicz","description":"Groovy closures overview: owner vs delegate vs this.","archived":false,"fork":false,"pushed_at":"2018-11-19T13:16:37.000Z","size":82,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-07-17T18:16:01.387Z","etag":null,"topics":["closure","closures","groovy"],"latest_commit_sha":null,"homepage":"","language":"Groovy","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtumilowicz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-11-13T22:56:07.000Z","updated_at":"2022-11-07T14:25:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"a7e71d21-2315-4b0c-93a8-344f320f788a","html_url":"https://github.com/mtumilowicz/groovy-closure-owner-delegate-this","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtumilowicz/groovy-closure-owner-delegate-this","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgroovy-closure-owner-delegate-this","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgroovy-closure-owner-delegate-this/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgroovy-closure-owner-delegate-this/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgroovy-closure-owner-delegate-this/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtumilowicz","download_url":"https://codeload.github.com/mtumilowicz/groovy-closure-owner-delegate-this/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fgroovy-closure-owner-delegate-this/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271103003,"owners_count":24699638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-19T02:00:09.176Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["closure","closures","groovy"],"created_at":"2025-01-04T22:51:12.926Z","updated_at":"2025-08-19T04:44:39.601Z","avatar_url":"https://github.com/mtumilowicz.png","language":"Groovy","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.com/mtumilowicz/groovy-closure-owner-delegate-this.svg?branch=master)](https://travis-ci.com/mtumilowicz/groovy-closure-owner-delegate-this)\n\n# groovy-closure-owner-delegate-this\nGroovy closures overview: owner vs delegate vs this.\n\n_Reference_: http://groovy-lang.org/closures.html#closure-owner  \n\nPlease refer also my other github projects about closures:\n* https://github.com/mtumilowicz/groovy-dsl\n* https://github.com/mtumilowicz/groovy-dsl-statemachine\n\n# preface\n## definition\nA closure in Groovy is **an open, anonymous, block of code** that can \ntake arguments, return a value and be assigned to a variable. A closure \nmay reference variables declared in its surrounding scope. In opposition to \nthe formal definition of a closure, Closure in the Groovy language **can \nalso contain free variables which are defined outside of its surrounding \nscope**. While breaking the formal concept of a closure, it offers a \nvariety of advantages which are described in this chapter.\n\n## syntax\nA closure definition follows this syntax:\n\n`{ [closureParameters -\u003e ] statements }`\n\nWhere `[closureParameters-\u003e]` is an optional comma-delimited list of \nparameters, and statements are 0 or more Groovy statements.\n\n## Groovy closures vs lambda expressions\nGroovy defines closures as instances of the Closure class. It makes \nit very different from lambda expressions in Java 8. Delegation is a \nkey concept in Groovy closures which has no equivalent in lambdas.\n\n# Owner, delegate and this\n_Reference_: Thanks to https://github.com/JackKarichkovskiy for\nhelping me with described below intricacies.\n\n* **this** corresponds to the enclosing class where the closure is \ndefined.\n* **owner** corresponds to the enclosing object where the closure is \ndefined, which may be either a class or a closure.\n* **delegate** corresponds to a third party object where methods \ncalls or properties are resolved whenever the receiver of the message \nis not defined.\n\n## this\n* if the closure is defined in an inner class\n`this` in the closure will return the inner class, not the top-level one\n* in case of nested closures - `this` corresponds to the closest outer \nclass, not the enclosing closure!\n\nTests are in `ThisTest`:\n* `this` inside closure\n    ```\n    given:\n    Closure closure = { this }\n    \n    expect:\n    closure() == this\n    closure().getClass() == ThisTest.class\n    ```\n* `this` inside closure inside closure\n    ```\n    given:\n    Closure closure = {\n        Closure inner = { this }\n        return inner\n    }\n    \n    expect:\n    closure()() == this\n    closure()().getClass() == ThisTest.class\n    ```\n* `this` inside closure inside inner class\n    ```\n    class InnerClass {\n        Closure inner = { this }\n    }\n    ```\n    ```\n    given:\n    def innerClass = new InnerClass()\n    \n    expect:\n    innerClass.inner() == innerClass\n    innerClass.inner().getClass() == InnerClass.class \n    ```\n\n## owner\nThe owner of a closure is very similar to the definition of this \nin a closure with a subtle difference: **it will return the direct \nenclosing object, be it a closure or a class**.\n\n* if the closure is defined in a inner class\n`owner` in the closure will return the inner class, not the top-level one\n* in case of nested closures `owner` corresponds to the enclosing \nclosure, hence a different object from `this`!\n\nTests are in `OwnerTest`:\n* `owner` inside closure\n    ```\n    given:\n    Closure closure = { owner }\n    \n    expect:\n    closure() == this\n    closure().getClass() == OwnerTest.class\n    ```\n* `owner` inside closure inside closure\n    ```\n    given:\n    Closure closure = {\n        Closure inner = { owner }\n        return inner\n    }\n    \n    expect:\n    closure()() == closure\n    closure()().getClass() == closure.getClass()\n    ```\n* `owner` inside closure inside inner class\n    ```\n    class InnerClass {\n        Closure inner = { owner }\n    }    \n    ```\n    ```\n    given:\n    def innerClass = new InnerClass()\n    \n    expect:\n    innerClass.inner() == innerClass\n    innerClass.inner().getClass() == InnerClass.class\n    ```\n\n## delegate\nWhile `closure-this` and `closure-owner` refer to the lexical scope of a \nclosure, the delegate is a user defined object that a closure will use. \nBy default, the delegate is set to `owner`.\n\nTests are in `DelegateTest` and are similar to `OwnerTest`\n\n# mixing this-owner-delegate\nTests are in `DelegateOwnerThisTest`. \n\nIn general - we test order  and scope of loading values used in closures.\n\nWe have couple of util classes (used during testing):\n```\nclass Delegate {\n    String value = \"fromDelegate\"\n\n    String methodFromDelegate(String string) {\n        string\n    }\n}\n```\n```\nclass Owner {\n    String value = \"fromOwner\"\n}\n```\n```\nclass This {\n    String value = \"fromThis\"\n}\n```\n```\nclass EmptyOwner {\n}\n```\n\nand we use rehydrate method from Closure class:\n\u003e Returns a copy of this closure for which the delegate, owner and thisObject are\nreplaced with the supplied parameters.\n```\npublic Closure\u003cV\u003e rehydrate(Object delegate, Object owner, Object thisObject)\n```\n\nOrder and scope of loading values used in closures:\n\n* local variables are used first\n    ```\n    given:\n    def value = \"this method\"\n    \n    and:\n    def closure = {\n        methodFromDelegate(value)\n    }\n    \n    when:\n    def rehydratedClosure = closure.rehydrate(new Delegate(), new Owner(), new This())\n    \n    then:\n    rehydratedClosure() == \"this method\"\n    ```\n* when we use `this.value` in a closure, the `this` is resolved first\n    ```\n    given:\n    def closure = {\n        methodFromDelegate(this.value)\n    }\n    \n    when:\n    def rehydratedClosure = closure.rehydrate(new Delegate(), new Owner(), new This())\n    \n    then:\n    rehydratedClosure() == \"fromThis\"\n    ```\n* when we use `value` in a closure, the `owner` is resolved first\n    ```\n    given:\n    def closure = {\n        methodFromDelegate(value)\n    }\n    \n    when:\n    def rehydratedClosure = closure.rehydrate(new Delegate(), new Owner(), new This())\n    \n    then:\n    rehydratedClosure() == \"fromOwner\"\n    ```\n* when we use `value` in a closure, the `owner` is resolved first (if `value` is not found)\nthen `delegate` is resolved\n    ```\n    given:\n    def closure = {\n        methodFromDelegate(value)\n    }\n    \n    when:\n    def rehydratedClosure = closure.rehydrate(new Delegate(), new EmptyOwner(), new This())\n    \n    then:\n    rehydratedClosure() == \"fromDelegate\"\n    ```\n\n# resolving strategies\n## summary\n**Note that local variables are always looked up first, \nindependently of the resolution strategy.**\n\n* **OWNER_FIRST** - the closure will attempt \nto resolve property references and methods to the owner first, then \nthe delegate - **this is the default strategy**.\n\n* **DELEGATE_FIRST** - the closure will attempt to resolve property \nreferences and methods to the delegate first then the owner.\n\n* **OWNER_ONLY** - the closure will resolve property \nreferences and methods to the owner only and not call the delegate \nat all.\n\n* **DELEGATE_ONLY** - the closure will resolve property \nreferences and methods to the delegate only and entirely bypass \nthe owner.\n\n* **TO_SELF** - the closure will resolve property references to \nitself and go through the usual MetaClass look-up process. This \nmeans that properties and methods are neither resolved from the \nowner nor the delegate, but only on the closure object itself. \nThis allows the developer to override getProperty using \n`ExpandoMetaClass` of the closure itself.\n\n* **Note that local variables are always looked up first, \nindependently of the resolution strategy.**\n## tests\nTests are in `ResolvingStrategiesTest`. They are quite straightforward.\nThe most interesting is testing `Closure.TO_SELF` strategy, because\nwe use `ExpandoMetaClass` to show key feature:\n```\ngiven:\nExpandoMetaClass.enableGlobally()\n\nand:\ndef closure = {\n    value\n}\n\nand:\nclosure.metaClass.value = \"inClosure\"\n\nwhen:\nclosure.resolveStrategy = Closure.TO_SELF\n\nthen:\nclosure() == \"inClosure\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fgroovy-closure-owner-delegate-this","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtumilowicz%2Fgroovy-closure-owner-delegate-this","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fgroovy-closure-owner-delegate-this/lists"}