Design by contract support for Groovy with @Invariant, @Requires, and @Ensures annotations
npx @tessl/cli install tessl/maven-org-apache-groovy--groovy-contracts@5.0.0Groovy Contracts provides design-by-contract support for the Groovy programming language through annotations that define class invariants, method preconditions, and postconditions. These contracts are automatically enforced at runtime through AST transformations that inject Java assertion statements, enabling explicit specification and verification of method contracts.
dependencies {
implementation 'org.apache.groovy:groovy-contracts:5.0.0'
}<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-contracts</artifactId>
<version>5.0.0</version>
</dependency>import groovy.contracts.*@Contracted
package com.example.contracts
import groovy.contracts.*
@Invariant({ elements != null && size >= 0 })
class Stack {
private def elements = []
private int size = 0
@Requires({ element != null })
@Ensures({ result == old.size + 1 })
def push(element) {
elements.add(element)
size++
return size
}
@Requires({ !isEmpty() })
@Ensures({ result != null && size == old.size - 1 })
def pop() {
def result = elements.removeLast()
size--
return result
}
def isEmpty() {
return size == 0
}
}Groovy Contracts is built around several key components:
Core annotations for defining design-by-contract specifications on classes and methods. These annotations use closure expressions to define contract conditions.
@Target([ElementType.PACKAGE, ElementType.TYPE])
@interface Contracted
@Target([ElementType.TYPE])
@Repeatable(Invariants.class)
@interface Invariant {
Class value()
}
@Target([ElementType.CONSTRUCTOR, ElementType.METHOD])
@Repeatable(RequiresConditions.class)
@interface Requires {
Class value()
}
@Target([ElementType.CONSTRUCTOR, ElementType.METHOD])
@Repeatable(EnsuresConditions.class)
@interface Ensures {
Class value()
}Exception classes thrown when contract violations occur, with specialized types for different violation scenarios and utilities for tracking multiple violations.
abstract class AssertionViolation extends AssertionError
class PreconditionViolation extends AssertionViolation
class PostconditionViolation extends AssertionViolation
class ClassInvariantViolation extends AssertionViolation
class CircularAssertionCallException extends RuntimeException
class ViolationTracker {
static ThreadLocal<ViolationTracker> INSTANCE
static void init()
static void deinit()
static boolean violationsOccurred()
static void rethrowFirst()
static void rethrowLast()
}@interface Invariants {
Invariant[] value()
}
@interface RequiresConditions {
Requires[] value()
}
@interface EnsuresConditions {
Ensures[] value()
}Special variables available in contract expressions:
result (return value), and old (map of previous field values)The old variable in postconditions supports automatic cloning for:
Cloneable typesBigInteger and BigDecimalString and GStringContracts are implemented as Java assertion statements and can be controlled using standard JVM assertion parameters:
-ea or -enableassertions-da or -disableassertions-ea:com.example.contracts.*Important: All contract annotations (except @Contracted) are marked with @Incubating, indicating they are subject to breaking changes in future versions until the API is stabilized. The exception classes and ViolationTracker are not marked as incubating and are considered stable.