Package: groovy.transform

## [Java] Annotation Type PackedClosures

- groovy.transform.PackedClosures

-     @Incubating
      @Retention(RetentionPolicy.SOURCE)
      @Target({ElementType.TYPE, ElementType.METHOD})
      public @interface PackedClosures

  Opt-in for compact closure compilation: within the annotated class or method, an eligible closure literal's body is hoisted into a synthetic method on the enclosing class and the literal is replaced by a shared PackedClosure adapter, instead of generating one inner class per closure. The value stays a real `groovy.lang.Closure`, so `curry`, `memoize`, `trampoline` and iteration keep working; captured values are threaded by value (read-only) or via a shared `groovy.lang.Reference` (when written), so a packed closure behaves identically to the class-based form.

  Packing is best-effort: a closure that cannot be packed is compiled exactly as today (a generated closure class), so the annotation is always safe to add. A closure is kept as a class when it:

  - references `owner`, `delegate`, `thisObject`, `resolveStrategy`, `directive`, `metaClass` or `super` — it needs a real `Closure`;
  - has default parameter values, or contains an anonymous inner class;
  - is nested inside another closure, or visibly escapes its method (returned, stored to a field/property/index, initialising a field, appended, or placed in a collection literal);
  - is visibly serialization-bound (cast or coerced to a `Serializable` type, passed directly to a `writeObject` call, or held in a local that is) — it keeps its class so serialization works;
  - is written where the adapter cannot stand in for a generated class: as an argument to a `this(...)`/`super(...)` constructor call, inside a trait, or cast to an intersection type;
  - under dynamic compilation, resolves any free name — an implicit-this call, a bare field/property-bound or dynamic variable, or an explicit `this`-property — that the delegate chain or the MOP could intercept;
  - under `@CompileStatic`, resolves a name against a delegate (e.g. via `@DelegatesTo` or `with`), is left to runtime resolution by a type-checking extension (mixed-mode dynamic islands), or touches `this`-properties of a `Map`-implementing owner — packing is otherwise proven sound from the type checker's resolution.
  Under dynamic compilation the annotation is a trust assertion the compiler cannot verify; the `PackedClosure` adapter then fails fast if a delegate, or a delegate-consulting `resolveStrategy`, is later set on a packed closure. Automatic packing of provably-safe `@CompileStatic` closures — without this annotation — is available behind the `groovy.target.closure.pack` flag. Use mode() to have declines reported (or to opt a scope out); on the un-annotated flag path, `groovy.target.closure.pack.report=true` reports every decline with its reason as a compiler warning — the operational way to see where the packability boundary falls in a codebase.

  Because every packed closure is an instance of a small fixed-arity adapter family (`PackedClosure$Fixed0..4`, `$FixedIt`, `$FixedN`) rather than its own class, three differences from the class-based form remain that cannot, in general, be detected at compile time (the visibly serialization-bound cases above are the detectable exception):

  - Serialization: a packed closure is not serializable — attempting it fails fast at runtime with a message naming the closure and this opt-out (note `dehydrate()` cannot make it serializable — the dispatch state remains — although a dehydrated packed closure stays callable), so scopes that serialize their closures should not be packed;
  - Class identity: `closure.getClass()` distinguishes arity (enough for class-level introspection such as SAM-overload selection) but not individual literals, so code keyed on per-closure generated class names or types will not find them;
  - Class-level metaclass changes: modifying a family member's metaclass affects every packed closure of that arity, where a per-closure-class change was scoped to one literal. Per-instance `setMetaClass` is fully honoured — a packed closure whose metaclass has been replaced or wrapped routes all dispatch through it, exactly as a generated closure class does.
  See Also:
    PackMode

- - ### Element Summary

    Optional Element Summary
    Type | Name and Description
    --- | ---
    `PackedClosures.PackMode` | `mode` The packing behaviour of this scope: PackMode.LENIENT (pack, silent on declines, the default), PackMode.WARN (pack, a compiler warning per decline with the reason), PackMode.STRICT (pack, a compiler error on any decline), or PackMode.DISABLED (do not pack).

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class Object` | `equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Element Detail

    - #### public PackedClosures.PackMode mode

      The packing behaviour of this scope: PackMode.LENIENT (pack, silent on declines, the default), PackMode.WARN (pack, a compiler warning per decline with the reason), PackMode.STRICT (pack, a compiler error on any decline), or PackMode.DISABLED (do not pack). The most-specific declaration wins, so a `DISABLED` method opts out of a packed class (and `DISABLED` also overrides the automatic `groovy.target.closure.pack` flag); the WARN/STRICT diagnostics apply only to the annotated scope, never to the flag.

      Default:
        PackMode.LENIENT