0
# AST Transformations
1
2
Groovy's AST (Abstract Syntax Tree) transformations provide compile-time code generation and modification through annotations, enabling powerful metaprogramming capabilities without runtime overhead.
3
4
## Code Generation Transforms
5
6
### @ToString
7
8
Automatically generates toString() method implementation.
9
10
```groovy { .api }
11
@interface ToString {
12
String[] excludes() default {}
13
String[] includes() default {}
14
boolean includeNames() default false
15
boolean includeFields() default false
16
boolean includeSuper() default false
17
boolean includeSuperProperties() default false
18
boolean ignoreNulls() default false
19
boolean includePackage() default false
20
boolean cache() default false
21
boolean allProperties() default false
22
}
23
```
24
25
Usage examples:
26
```groovy
27
import groovy.transform.ToString
28
29
@ToString
30
class Person {
31
String name
32
int age
33
String email
34
}
35
36
@ToString(includeNames=true, excludes=['email'])
37
class Employee {
38
String name
39
int age
40
String email
41
String department
42
}
43
44
def person = new Person(name: 'John', age: 30, email: 'john@example.com')
45
println person.toString() // Person(John, 30, john@example.com)
46
47
def employee = new Employee(name: 'Jane', age: 25, email: 'jane@company.com', department: 'IT')
48
println employee.toString() // Employee(name:Jane, age:25, department:IT)
49
```
50
51
### @EqualsAndHashCode
52
53
Generates equals() and hashCode() methods with proper implementations.
54
55
```groovy { .api }
56
@interface EqualsAndHashCode {
57
String[] excludes() default {}
58
String[] includes() default {}
59
boolean callSuper() default false
60
boolean includeFields() default false
61
boolean cache() default false
62
boolean useCanEqual() default true
63
boolean allProperties() default false
64
}
65
```
66
67
Usage example:
68
```groovy
69
import groovy.transform.EqualsAndHashCode
70
71
@EqualsAndHashCode
72
class Point {
73
int x
74
int y
75
}
76
77
@EqualsAndHashCode(excludes=['id'])
78
class User {
79
String id
80
String name
81
String email
82
}
83
84
def p1 = new Point(x: 10, y: 20)
85
def p2 = new Point(x: 10, y: 20)
86
assert p1 == p2
87
assert p1.hashCode() == p2.hashCode()
88
```
89
90
### @TupleConstructor
91
92
Generates constructor accepting parameters for specified properties.
93
94
```groovy { .api }
95
@interface TupleConstructor {
96
String[] excludes() default {}
97
String[] includes() default {}
98
boolean includeFields() default false
99
boolean includeProperties() default true
100
boolean includeSuperFields() default false
101
boolean includeSuperProperties() default false
102
boolean callSuper() default false
103
boolean force() default false
104
boolean defaults() default true
105
boolean useSetters() default false
106
boolean allNames() default false
107
boolean allProperties() default false
108
}
109
```
110
111
Usage example:
112
```groovy
113
import groovy.transform.TupleConstructor
114
115
@TupleConstructor
116
class Book {
117
String title
118
String author
119
int year
120
}
121
122
@TupleConstructor(includes=['name', 'age'])
123
class Person {
124
String name
125
int age
126
String email = 'unknown@example.com'
127
}
128
129
def book = new Book('1984', 'George Orwell', 1949)
130
def person = new Person('John', 30)
131
```
132
133
## Design Pattern Transforms
134
135
### @Singleton
136
137
Implements the Singleton design pattern with various strategies.
138
139
```groovy { .api }
140
@interface Singleton {
141
String property() default 'instance'
142
boolean lazy() default true
143
boolean strict() default true
144
}
145
```
146
147
Usage examples:
148
```groovy
149
import groovy.transform.Singleton
150
151
@Singleton
152
class DatabaseConnection {
153
String url = 'jdbc:h2:mem:testdb'
154
155
void connect() {
156
println "Connecting to $url"
157
}
158
}
159
160
@Singleton(lazy=false, strict=false)
161
class Logger {
162
void log(String message) {
163
println "[${new Date()}] $message"
164
}
165
}
166
167
// Usage
168
DatabaseConnection.instance.connect()
169
Logger.instance.log('Application started')
170
```
171
172
### @Immutable
173
174
Creates immutable classes with defensive copying and validation.
175
176
```groovy { .api }
177
@interface Immutable {
178
String[] excludes() default {}
179
String[] includes() default {}
180
boolean copyWith() default false
181
boolean knownImmutableClasses() default false
182
String[] knownImmutables() default {}
183
}
184
```
185
186
Usage example:
187
```groovy
188
import groovy.transform.Immutable
189
190
@Immutable
191
class Point3D {
192
double x, y, z
193
}
194
195
@Immutable(copyWith=true)
196
class Person {
197
String name
198
int age
199
List<String> hobbies
200
}
201
202
def point = new Point3D(1.0, 2.0, 3.0)
203
def person = new Person('John', 30, ['reading', 'swimming'])
204
205
// Create modified copies
206
def olderPerson = person.copyWith(age: 31)
207
```
208
209
### @Builder
210
211
Implements the Builder pattern for object construction.
212
213
```groovy { .api }
214
@interface Builder {
215
String builderClassName() default ''
216
String builderMethodName() default 'builder'
217
String buildMethodName() default 'build'
218
String prefix() default ''
219
boolean includeSuperProperties() default false
220
boolean useSetters() default false
221
String[] excludes() default {}
222
String[] includes() default {}
223
boolean allNames() default false
224
boolean allProperties() default false
225
}
226
```
227
228
Usage example:
229
```groovy
230
import groovy.transform.builder.Builder
231
import groovy.transform.builder.ExternalStrategy
232
233
@Builder
234
class Computer {
235
String cpu
236
String memory
237
String storage
238
String graphics
239
}
240
241
@Builder(builderStrategy=ExternalStrategy, forClass=Person)
242
class PersonBuilder {}
243
244
// Usage
245
def computer = Computer.builder()
246
.cpu('Intel i7')
247
.memory('16GB')
248
.storage('1TB SSD')
249
.graphics('NVIDIA RTX')
250
.build()
251
```
252
253
## Behavior Modification Transforms
254
255
### @CompileStatic
256
257
Enables static compilation for improved performance and type safety.
258
259
```groovy { .api }
260
@interface CompileStatic {
261
TypeCheckingMode value() default TypeCheckingMode.PASS
262
String[] extensions() default {}
263
}
264
```
265
266
Usage example:
267
```groovy
268
import groovy.transform.CompileStatic
269
270
@CompileStatic
271
class Calculator {
272
int add(int a, int b) {
273
return a + b // Statically compiled
274
}
275
276
double multiply(double x, double y) {
277
return x * y // Type-safe operations
278
}
279
}
280
```
281
282
### @TypeChecked
283
284
Enables static type checking without full static compilation.
285
286
```groovy { .api }
287
@interface TypeChecked {
288
TypeCheckingMode value() default TypeCheckingMode.PASS
289
String[] extensions() default {}
290
}
291
```
292
293
Usage example:
294
```groovy
295
import groovy.transform.TypeChecked
296
297
@TypeChecked
298
class StringProcessor {
299
String process(String input) {
300
return input.toUpperCase().trim() // Type-checked at compile time
301
}
302
}
303
```
304
305
### @Memoized
306
307
Caches method results for improved performance with repeated calls.
308
309
```groovy { .api }
310
@interface Memoized {
311
int protectedCacheSize() default 0
312
int maxCacheSize() default 0
313
}
314
```
315
316
Usage example:
317
```groovy
318
import groovy.transform.Memoized
319
import groovy.transform.CompileStatic
320
321
@CompileStatic
322
class FibonacciCalculator {
323
@Memoized
324
long fibonacci(int n) {
325
if (n <= 1) return n
326
return fibonacci(n - 1) + fibonacci(n - 2)
327
}
328
329
@Memoized(maxCacheSize=100)
330
double expensiveCalculation(double input) {
331
// Simulate expensive computation
332
Thread.sleep(1000)
333
return Math.pow(input, 3) + Math.sin(input)
334
}
335
}
336
```
337
338
### @Synchronized
339
340
Provides method-level synchronization with configurable lock objects.
341
342
```groovy { .api }
343
@interface Synchronized {
344
String value() default ''
345
}
346
```
347
348
Usage example:
349
```groovy
350
import groovy.transform.Synchronized
351
352
class Counter {
353
private int count = 0
354
private final Object lockA = new Object()
355
private final Object lockB = new Object()
356
357
@Synchronized
358
void increment() {
359
count++
360
}
361
362
@Synchronized('lockA')
363
void methodA() {
364
// Uses lockA for synchronization
365
}
366
367
@Synchronized('lockB')
368
void methodB() {
369
// Uses lockB for synchronization
370
}
371
372
@Synchronized
373
int getCount() {
374
return count
375
}
376
}
377
```
378
379
## Metaprogramming Transforms
380
381
### @Delegate
382
383
Implements the Delegation pattern by forwarding method calls.
384
385
```groovy { .api }
386
@interface Delegate {
387
Class[] excludeTypes() default {}
388
String[] excludes() default {}
389
String[] includes() default {}
390
boolean deprecated() default false
391
boolean allNames() default false
392
boolean methodAnnotations() default false
393
boolean parameterAnnotations() default false
394
boolean interfaces() default true
395
}
396
```
397
398
Usage example:
399
```groovy
400
import groovy.transform.Delegate
401
402
class EventManager {
403
@Delegate List<String> events = []
404
@Delegate(excludes=['clear']) Map<String, Object> properties = [:]
405
406
void logEvent(String event) {
407
println "Event logged: $event"
408
events.add(event)
409
}
410
}
411
412
def manager = new EventManager()
413
manager.add('startup') // Delegated to List
414
manager.put('version', '1.0') // Delegated to Map
415
// manager.clear() // Not available due to excludes
416
```
417
418
### @Category
419
420
Creates category classes for adding methods to existing types.
421
422
```groovy { .api }
423
@interface Category {
424
Class value()
425
}
426
```
427
428
Usage example:
429
```groovy
430
import groovy.transform.Category
431
432
@Category(String)
433
class StringExtensions {
434
boolean isPalindrome() {
435
return this == this.reverse()
436
}
437
438
String toCamelCase() {
439
return this.tokenize('_').collect {
440
it.toLowerCase().capitalize()
441
}.join('')
442
}
443
}
444
445
// Usage
446
use(StringExtensions) {
447
assert 'racecar'.isPalindrome()
448
assert 'hello_world'.toCamelCase() == 'HelloWorld'
449
}
450
```
451
452
### @Mixin
453
454
Adds mixin capabilities to classes.
455
456
```groovy { .api }
457
@interface Mixin {
458
Class[] value()
459
}
460
```
461
462
### @Trait
463
464
Defines and uses traits for multiple inheritance of behavior.
465
466
```groovy { .api }
467
@interface Trait {}
468
```
469
470
Usage example:
471
```groovy
472
trait Flyable {
473
void fly() {
474
println "${this.class.simpleName} is flying"
475
}
476
}
477
478
trait Swimmable {
479
void swim() {
480
println "${this.class.simpleName} is swimming"
481
}
482
}
483
484
class Duck implements Flyable, Swimmable {
485
String name
486
}
487
488
class Fish implements Swimmable {
489
String species
490
}
491
492
def duck = new Duck(name: 'Donald')
493
duck.fly() // Duck is flying
494
duck.swim() // Duck is swimming
495
496
def fish = new Fish(species: 'Goldfish')
497
fish.swim() // Fish is swimming
498
```
499
500
## Utility Transforms
501
502
### @Canonical
503
504
Combines @ToString, @EqualsAndHashCode, and @TupleConstructor.
505
506
```groovy { .api }
507
@interface Canonical {
508
String[] excludes() default {}
509
String[] includes() default {}
510
boolean includeFields() default false
511
boolean includeProperties() default true
512
boolean includeSuperFields() default false
513
boolean includeSuperProperties() default false
514
boolean callSuper() default false
515
boolean force() default false
516
boolean useSetters() default false
517
boolean allNames() default false
518
boolean allProperties() default false
519
}
520
```
521
522
Usage example:
523
```groovy
524
import groovy.transform.Canonical
525
526
@Canonical
527
class Product {
528
String name
529
BigDecimal price
530
String category
531
}
532
533
def product1 = new Product('Laptop', 999.99, 'Electronics')
534
def product2 = new Product('Laptop', 999.99, 'Electronics')
535
536
assert product1 == product2
537
println product1.toString()
538
```
539
540
### @InheritConstructors
541
542
Inherits constructors from the superclass.
543
544
```groovy { .api }
545
@interface InheritConstructors {
546
boolean constructorAnnotations() default false
547
boolean parameterAnnotations() default false
548
}
549
```
550
551
Usage example:
552
```groovy
553
import groovy.transform.InheritConstructors
554
555
class CustomException extends RuntimeException {
556
// Inherits all RuntimeException constructors
557
}
558
559
@InheritConstructors
560
class MyList extends ArrayList {
561
// Inherits all ArrayList constructors
562
563
void customMethod() {
564
println "Custom functionality"
565
}
566
}
567
```
568
569
## Custom AST Transformations
570
571
### Creating Custom Transforms
572
573
```groovy
574
import org.codehaus.groovy.transform.GroovyASTTransformation
575
import org.codehaus.groovy.transform.ASTTransformation
576
import org.codehaus.groovy.control.CompilePhase
577
import org.codehaus.groovy.control.SourceUnit
578
import org.codehaus.groovy.ast.ASTNode
579
import org.codehaus.groovy.ast.ClassNode
580
import org.codehaus.groovy.ast.MethodNode
581
582
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
583
class LogMethodCallsTransformation implements ASTTransformation {
584
585
void visit(ASTNode[] nodes, SourceUnit source) {
586
// Custom transformation logic
587
nodes.each { node ->
588
if (node instanceof ClassNode) {
589
addLoggingToMethods(node)
590
}
591
}
592
}
593
594
private void addLoggingToMethods(ClassNode classNode) {
595
classNode.methods.each { MethodNode method ->
596
// Add logging statements to method bodies
597
// Implementation details...
598
}
599
}
600
}
601
602
// Usage annotation
603
@interface LogMethodCalls {}
604
```
605
606
### Local AST Transformations
607
608
```groovy
609
import org.codehaus.groovy.transform.GroovyASTTransformationClass
610
import java.lang.annotation.ElementType
611
import java.lang.annotation.Target
612
import java.lang.annotation.Retention
613
import java.lang.annotation.RetentionPolicy
614
615
@Retention(RetentionPolicy.SOURCE)
616
@Target([ElementType.TYPE])
617
@GroovyASTTransformationClass(["com.example.LogMethodCallsTransformation"])
618
@interface LogMethodCalls {}
619
620
// Apply to classes
621
@LogMethodCalls
622
class MyService {
623
void processData() {
624
// Method calls will be logged automatically
625
}
626
}
627
```
628
629
## Error Handling and Debugging
630
631
### Compilation Errors
632
633
```groovy
634
// Common AST transformation errors and solutions
635
636
// 1. Conflicting transformations
637
@ToString
638
@CompileStatic
639
class Example {
640
// Some combinations may cause issues
641
}
642
643
// 2. Missing dependencies
644
@Builder // Requires specific dependencies
645
class MyClass {
646
String property
647
}
648
649
// 3. Incorrect annotation usage
650
@Singleton(property="wrongType") // Should be String
651
class BadSingleton {}
652
```
653
654
### Debugging AST Transformations
655
656
```groovy
657
// Enable AST transformation debugging
658
System.setProperty("groovy.ast.debug", "true")
659
660
// Use AST viewer tools to inspect generated code
661
import org.codehaus.groovy.ast.ClassNode
662
import org.codehaus.groovy.control.CompilerConfiguration
663
664
def config = new CompilerConfiguration()
665
config.debug = true
666
config.verbose = true
667
668
// Examine generated bytecode
669
import groovy.transform.ToString
670
671
@ToString
672
class DebugExample {
673
String name
674
int value
675
}
676
677
// The generated toString method can be inspected
678
def example = new DebugExample(name: 'test', value: 42)
679
println example.toString()
680
```