CodeToLive

Groovy Metaprogramming

Metaprogramming in Groovy allows you to modify classes and objects at runtime, adding or changing behavior dynamically.

Runtime Metaprogramming

Add methods to classes or individual objects at runtime:

// Add method to String class
String.metaClass.shout = { -> delegate.toUpperCase() + "!" }
println "hello".shout()  // "HELLO!"

// Add method to a specific instance
def person = new Person(name: 'Alice')
person.metaClass.greet = { "Hello, ${delegate.name}!" }
println person.greet()   // "Hello, Alice!"

Expando Objects

Dynamic objects that can have properties and methods added at runtime:

def person = new Expando()
person.name = 'Alice'
person.greet = { "Hello, ${person.name}!" }

println person.greet()  // "Hello, Alice!"

Method Missing

Handle calls to non-existent methods:

class DynamicPerson {
    def methodMissing(String name, args) {
        "Called $name with ${args.join(', ')}"
    }
}

def p = new DynamicPerson()
println p.sayHello('Alice')  // "Called sayHello with Alice"

Property Missing

Handle access to non-existent properties:

class DynamicConfig {
    def propertyMissing(String name) {
        "Property $name not found"
    }
}

def config = new DynamicConfig()
println config.serverUrl  // "Property serverUrl not found"

Compile-time Metaprogramming (AST Transformations)

Modify the AST (Abstract Syntax Tree) during compilation:

@ToString
@EqualsAndHashCode
@TupleConstructor
class Person {
    String name
    int age
}

// The annotations above generate toString(), equals(), hashCode() 
// and constructors at compile time

Common AST Transformations

  • @Immutable: Creates immutable class
  • @Delegate: Delegates methods to a field
  • @Singleton: Creates a singleton
  • @Log: Adds logger field
  • @Builder: Creates builder pattern

Creating Custom AST Transformations

You can create your own AST transformations for code generation:

@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.TYPE])
@GroovyASTTransformationClass("com.example.TimestampASTTransformation")
public @interface Timestamp {}
← Back to Tutorials