Package: groovy.contracts

## [Java] Annotation Type ThrowsIf

- groovy.contracts.ThrowsIf

-     @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
      @Incubating
      @Repeatable(ThrowsIfConditions.class)
      public @interface ThrowsIf

  Represents a method's exceptional contract: the method throws the given exception when the given condition holds on entry.

       @ThrowsIf(value = { b == 0 }, exception = ArithmeticException)
       int divide(int a, int b) { a.intdiv(b) }

  This is the exceptional counterpart of Requires: a precondition says the caller must not do this (violating it is the caller's bug), whereas a `@ThrowsIf` arm says this input is handled, by throwing — it is defined behaviour, part of the contract, that callers may rely on (and catch). The condition is a closure over the method's parameters, using the same conventions as Requires.

  Weaving. With the default `woven = true` the guard-throw is generated at method entry — the general form of the pattern `groovy.transform.NullCheck` provides for the null-check special case — so the annotation is the implementation, not a comment about one:

       if (b == 0) throw new ArithmeticException(...)   // inserted; message derived from the condition

  On a constructor whose first statement is an explicit `super(...)` or `this(...)` call, the guard is inserted immediately after that call, which must stay first — a language constraint.

  With `woven = false` nothing is generated: the throw already exists. Where it exists is the `direct` member — pure information for readers and tools, with no effect on bytecode. `direct = true` (the default): a hand-written `throw` statement lives in this body — checkable documentation of a guard that is already there. `direct = false`: the exception arises from code this method executes — a call, possibly transitively (a third-party library), or a runtime operation — so there is no throw statement in this body to find (and weaving a wrong specification about invoked code would silently change behaviour, which is why such arms are spelled `woven = false`). For woven code `direct` is implicitly true and the member is ignored — most users never set it; a tool that cannot find the promised throw in the body is the usual prompt to add `direct = false`. The attributes are per-arm because real methods mix modes:

       @ThrowsIf(value = { x == null }, exception = NullPointerException)                 // woven for x
       @ThrowsIf(value = { y == null }, exception = NullPointerException, woven = false)  // body guards y
       Object process(Object x, Object y) { ... }

  Semantics. Read as an iff by default: the method throws a matching exception exactly when some arm's condition holds — each arm is a must-throw (condition on entry ⇒ the method throws, not returns), and the arm-set as a whole claims only-when (a matching throw ⇒ some arm's condition held). `exhaustive = false` disclaims the only-when half for the whole arm-set (a one-directional, JML `signals`-style arm): the condition is sufficient for the throw but the set does not claim to list every reason — useful when the full condition is inexpressible (for example, `Integer.parseInt`: `s == null` is a true sufficient condition, while "malformed or out of range" is not reasonably a parameter closure). Note what neither mode claims: exhaustiveness is (at most) over the conditions for the exception types mentioned, never over exception types themselves — declaring an `ArithmeticException` arm says nothing about whether the method can throw anything else. And no claim, however exhaustive, reasons about VM resource conditions: an `OutOfMemoryError` or `StackOverflowError` is outside contract semantics — the checked wrapper passes any VirtualMachineError through unjudged, whatever the arm types.

  Checking. Weaving implements the contract; `checked = true` additionally verifies it at runtime, in the same assertion style as Ensures: on a normal return, no non-woven arm's condition may have held (must-throw), and an escaping exception matching some arm's type must be justified by a matching arm's condition having held on entry (only-when — checked only for `exhaustive` arm-sets). A broken implementation raises ThrowsIfViolation — never the declared exception, which is defined behaviour delivered at entry; a justified throw always propagates untouched. `checked` is set-level: if any arm is `checked`, the whole arm-set is checked — every non-woven arm is must-throw checked, and every arm serves as an only-when justifier (a sibling cannot silently opt out). A pleasant corollary: `woven = false, direct = false` plus `checked = true` runtime-validates a claim about invoked third-party code — a wrong specification is exposed, not silently believed.

  The annotation is runtime-retained and repeatable, so the exceptional contract is structured metadata available to documentation generators, static analysers, verification tools, and AI coding agents — none of which can reliably consume javadoc prose or discover guarded throw sites by body traversal. Only the must-throw guard of a woven arm and the `checked` wrapper affect generated code; `exception`, `exhaustive` and `direct` are otherwise consumed by tools.

  Since:
    6.0.0
  See Also:
    Requires
    ThrowsIfConditions

- - ### Element Summary

    Required Element Summary
    Type | Name and Description
    --- | ---
    `Class` | `value` Returns the closure class that evaluates the throw condition, a boolean expression over the method's parameters.

    Optional Element Summary
    Type | Name and Description
    --- | ---
    `boolean` | `checked` `true`: verify the contract at runtime in the assertion style of Ensures — a normal return with a non-woven arm's condition held, or an unjustified escaping throw of a matching type (`exhaustive` arm-sets only), raises a ThrowsIfViolation.
    `boolean` | `direct` Information for readers and tools, with no effect on bytecode; ignored (implicitly `true`) for woven arms.
    `Class<? extends Throwable>` | `exception` The exception type thrown when the condition holds.
    `boolean` | `exhaustive` No effect on generated code by itself.
    `boolean` | `woven` `true` (default): generate the guard-throw at method entry.

  - ### Inherited Methods Summary

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

- - ### Element Detail

    - #### public boolean checked

      `true`: verify the contract at runtime in the assertion style of Ensures — a normal return with a non-woven arm's condition held, or an unjustified escaping throw of a matching type (`exhaustive` arm-sets only), raises a ThrowsIfViolation. `false` (default): no runtime checking beyond any `WOVEN` guard.

      Returns:
        whether the contract is runtime-checked
      Default:
        false

    - #### public boolean direct

      Information for readers and tools, with no effect on bytecode; ignored (implicitly `true`) for woven arms. `true` (default): a hand-written `throw` statement lives in this body. `false`: the exception arises from code this method executes — a call, possibly transitively, or a runtime operation — so there is no throw statement in this body to find. Most users never set this; a verification or analysis tool unable to find the promised throw in the body is the usual prompt.

      Returns:
        whether the body itself contains the throw
      Default:
        true

    - #### public Class<? extends Throwable> exception

      The exception type thrown when the condition holds. A woven arm generates `new <exception>(String)`, so the type should provide a `(String)` constructor (all the standard JDK exceptions do).

      Returns:
        the exception type
      Default:
        Throwable.class

    - #### public boolean exhaustive

      No effect on generated code by itself. Under `checked = true` it gates the escaping-throw check: `true` (default) claims the listed conditions are the only reasons a matching exception is thrown, so an escaping match with no condition held is a ThrowsIfViolation; `false` says the method may throw the same exception for other, unlisted reasons — escaping throws are never judged (and verification tools likewise skip the only-when direction). One `false` arm disclaims the check for the whole arm-set. In specification terms: `true` is an iff, `false` a one-directional (JML `signals`-style) sufficient condition — the honest choice when the full condition is unstatable (`Integer.parseInt`: `s == null` is a true sufficient half; "malformed or out of range" is not a parameter closure).

      Returns:
        whether the arm-set claims to list every reason a matching exception is thrown
      Default:
        true

    - #### public Class value

      Returns the closure class that evaluates the throw condition, a boolean expression over the method's parameters.

      Returns:
        the generated closure class backing the condition

    - #### public boolean woven

      `true` (default): generate the guard-throw at method entry. `false`: the throw already exists (see `direct` for where) — nothing is generated.

      Returns:
        whether the guard is generated
      Default:
        true