0
# Transform Annotations
1
2
Compile-time code generation annotations that automatically add common functionality like toString(), equals(), builders, immutability, and static compilation support. These AST transformations reduce boilerplate code and enhance developer productivity.
3
4
## Capabilities
5
6
### Compilation Control
7
8
Annotations that control how Groovy code is compiled and type-checked.
9
10
```java { .api }
11
@Target({ElementType.TYPE, ElementType.METHOD})
12
@Retention(RetentionPolicy.SOURCE)
13
@interface CompileStatic {
14
/**
15
* List of extensions to apply during static compilation.
16
*/
17
Class<? extends ExtensionMethodNode>[] extensions() default {};
18
19
/**
20
* Type checking mode to use.
21
*/
22
TypeCheckingMode value() default TypeCheckingMode.PASS;
23
}
24
25
@Target({ElementType.TYPE, ElementType.METHOD})
26
@Retention(RetentionPolicy.SOURCE)
27
@interface TypeChecked {
28
/**
29
* List of extensions to apply during type checking.
30
*/
31
Class<? extends TypeCheckingExtension>[] extensions() default {};
32
33
/**
34
* Type checking mode to use.
35
*/
36
TypeCheckingMode value() default TypeCheckingMode.PASS;
37
}
38
39
@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD})
40
@Retention(RetentionPolicy.SOURCE)
41
@interface Field {
42
/**
43
* Makes script variables accessible as fields.
44
*/
45
}
46
47
@Target({ElementType.TYPE})
48
@Retention(RetentionPolicy.SOURCE)
49
@interface PackageScope {
50
/**
51
* Makes a class package-private instead of public.
52
*/
53
}
54
55
@Target({ElementType.TYPE})
56
@Retention(RetentionPolicy.SOURCE)
57
@interface CompileDynamic {
58
/**
59
* Disables static compilation for this type or method.
60
*/
61
}
62
```
63
64
### Class Generation
65
66
Annotations that generate common class functionality automatically.
67
68
```java { .api }
69
@Target({ElementType.TYPE})
70
@Retention(RetentionPolicy.SOURCE)
71
@interface Immutable {
72
/**
73
* Properties to exclude from immutable treatment.
74
*/
75
String[] excludes() default {};
76
77
/**
78
* Whether to include fields in addition to properties.
79
*/
80
boolean includeFields() default false;
81
82
/**
83
* Whether to generate a copyWith method.
84
*/
85
boolean copyWith() default false;
86
87
/**
88
* Known immutable classes.
89
*/
90
Class<?>[] knownImmutableClasses() default {};
91
92
/**
93
* Known immutable classes by name.
94
*/
95
String[] knownImmutables() default {};
96
}
97
98
@Target({ElementType.TYPE})
99
@Retention(RetentionPolicy.SOURCE)
100
@interface Canonical {
101
/**
102
* Properties to exclude from generated methods.
103
*/
104
String[] excludes() default {};
105
106
/**
107
* Whether to include fields in generated methods.
108
*/
109
boolean includeFields() default false;
110
111
/**
112
* Whether to cache the hash code.
113
*/
114
boolean cache() default false;
115
116
/**
117
* Whether to use super class in equals/hashCode.
118
*/
119
boolean useCanEqual() default true;
120
}
121
122
@Target({ElementType.TYPE})
123
@Retention(RetentionPolicy.SOURCE)
124
@interface ToString {
125
/**
126
* Properties to exclude from toString.
127
*/
128
String[] excludes() default {};
129
130
/**
131
* Whether to include field names in output.
132
*/
133
boolean includeNames() default false;
134
135
/**
136
* Whether to include fields in addition to properties.
137
*/
138
boolean includeFields() default false;
139
140
/**
141
* Whether to include super class in toString.
142
*/
143
boolean includeSuper() default false;
144
145
/**
146
* Whether to ignore null values.
147
*/
148
boolean ignoreNulls() default false;
149
150
/**
151
* Whether to include package name in class name.
152
*/
153
boolean includePackage() default false;
154
155
/**
156
* Whether to cache the toString result.
157
*/
158
boolean cache() default false;
159
}
160
161
@Target({ElementType.TYPE})
162
@Retention(RetentionPolicy.SOURCE)
163
@interface EqualsAndHashCode {
164
/**
165
* Properties to exclude from equals/hashCode.
166
*/
167
String[] excludes() default {};
168
169
/**
170
* Whether to include fields in addition to properties.
171
*/
172
boolean includeFields() default false;
173
174
/**
175
* Whether to call super in equals/hashCode.
176
*/
177
boolean callSuper() default false;
178
179
/**
180
* Whether to use canEqual method.
181
*/
182
boolean useCanEqual() default true;
183
184
/**
185
* Whether to cache the hash code.
186
*/
187
boolean cache() default false;
188
}
189
```
190
191
### Constructor Generation
192
193
Annotations that generate constructors automatically.
194
195
```java { .api }
196
@Target({ElementType.TYPE})
197
@Retention(RetentionPolicy.SOURCE)
198
@interface TupleConstructor {
199
/**
200
* Properties to exclude from constructor.
201
*/
202
String[] excludes() default {};
203
204
/**
205
* Properties to include in constructor.
206
*/
207
String[] includes() default {};
208
209
/**
210
* Whether to include fields in addition to properties.
211
*/
212
boolean includeFields() default false;
213
214
/**
215
* Whether to include super properties.
216
*/
217
boolean includeSuper() default false;
218
219
/**
220
* Whether to call super constructor.
221
*/
222
boolean callSuper() default false;
223
224
/**
225
* Whether to force generation even if constructors exist.
226
*/
227
boolean force() default false;
228
229
/**
230
* Default values for constructor parameters.
231
*/
232
String[] defaults() default {};
233
}
234
235
@Target({ElementType.TYPE})
236
@Retention(RetentionPolicy.SOURCE)
237
@interface InheritConstructors {
238
/**
239
* Whether to inherit constructors from parent class.
240
*/
241
boolean constructorAnnotations() default false;
242
243
/**
244
* Whether to copy parameter annotations.
245
*/
246
boolean parameterAnnotations() default false;
247
}
248
249
@Target({ElementType.TYPE})
250
@Retention(RetentionPolicy.SOURCE)
251
@interface MapConstructor {
252
/**
253
* Properties to exclude from map constructor.
254
*/
255
String[] excludes() default {};
256
257
/**
258
* Properties to include in map constructor.
259
*/
260
String[] includes() default {};
261
262
/**
263
* Whether to include fields in addition to properties.
264
*/
265
boolean includeFields() default false;
266
267
/**
268
* Whether to include super properties.
269
*/
270
boolean includeSuper() default false;
271
272
/**
273
* Whether to generate a no-arg constructor.
274
*/
275
boolean noArg() default false;
276
277
/**
278
* Post-processing closure.
279
*/
280
Class<? extends Closure> post() default Closure.class;
281
}
282
```
283
284
### Builder Pattern
285
286
Annotations for generating builder pattern implementations.
287
288
```java { .api }
289
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
290
@Retention(RetentionPolicy.SOURCE)
291
@interface Builder {
292
/**
293
* Builder strategy to use.
294
*/
295
Class<?> builderStrategy() default DefaultStrategy.class;
296
297
/**
298
* Builder class name prefix.
299
*/
300
String builderClassName() default "";
301
302
/**
303
* Builder method name.
304
*/
305
String builderMethodName() default "builder";
306
307
/**
308
* Build method name.
309
*/
310
String buildMethodName() default "build";
311
312
/**
313
* Properties to exclude from builder.
314
*/
315
String[] excludes() default {};
316
317
/**
318
* Properties to include in builder.
319
*/
320
String[] includes() default {};
321
322
/**
323
* Whether to include super properties.
324
*/
325
boolean includeSuper() default false;
326
327
/**
328
* Prefix for setter methods.
329
*/
330
String prefix() default "";
331
332
/**
333
* Whether to force generation.
334
*/
335
boolean force() default false;
336
}
337
```
338
339
### Field and Method Enhancement
340
341
Annotations that enhance fields and methods with additional behavior.
342
343
```java { .api }
344
@Target({ElementType.FIELD, ElementType.METHOD})
345
@Retention(RetentionPolicy.SOURCE)
346
@interface Lazy {
347
/**
348
* Whether to use soft references for lazy values.
349
*/
350
boolean soft() default false;
351
}
352
353
@Target({ElementType.FIELD})
354
@Retention(RetentionPolicy.SOURCE)
355
@interface Delegate {
356
/**
357
* Interfaces to delegate to.
358
*/
359
Class<?>[] interfaces() default {};
360
361
/**
362
* Methods to exclude from delegation.
363
*/
364
String[] excludes() default {};
365
366
/**
367
* Types of methods to exclude.
368
*/
369
String[] excludeTypes() default {};
370
371
/**
372
* Whether to include deprecated methods.
373
*/
374
boolean includeDeprecated() default true;
375
376
/**
377
* Whether to use parameter names in method signatures.
378
*/
379
boolean parameterAnnotations() default false;
380
381
/**
382
* Whether to copy method annotations.
383
*/
384
boolean methodAnnotations() default false;
385
}
386
387
@Target({ElementType.METHOD})
388
@Retention(RetentionPolicy.SOURCE)
389
@interface Memoized {
390
/**
391
* Maximum cache size.
392
*/
393
int maxCacheSize() default 0;
394
395
/**
396
* Whether to protect from recursion.
397
*/
398
boolean protectedFromRecursion() default false;
399
}
400
401
@Target({ElementType.METHOD})
402
@Retention(RetentionPolicy.SOURCE)
403
@interface TailRecursive {
404
/**
405
* Optimizes tail-recursive methods.
406
*/
407
}
408
```
409
410
### Synchronization
411
412
Annotations for thread-safe method execution.
413
414
```java { .api }
415
@Target({ElementType.METHOD})
416
@Retention(RetentionPolicy.SOURCE)
417
@interface Synchronized {
418
/**
419
* Lock field name to use for synchronization.
420
*/
421
String value() default "";
422
}
423
424
@Target({ElementType.METHOD})
425
@Retention(RetentionPolicy.SOURCE)
426
@interface WithReadLock {
427
/**
428
* Lock field name to use for read locking.
429
*/
430
String value() default "$reentrantlock";
431
}
432
433
@Target({ElementType.METHOD})
434
@Retention(RetentionPolicy.SOURCE)
435
@interface WithWriteLock {
436
/**
437
* Lock field name to use for write locking.
438
*/
439
String value() default "$reentrantlock";
440
}
441
```
442
443
### Bean Properties
444
445
Annotations for property binding and change notification.
446
447
```java { .api }
448
@Target({ElementType.FIELD, ElementType.TYPE})
449
@Retention(RetentionPolicy.SOURCE)
450
@interface Bindable {
451
/**
452
* Generates PropertyChangeSupport for bindable properties.
453
*/
454
}
455
456
@Target({ElementType.FIELD, ElementType.TYPE})
457
@Retention(RetentionPolicy.SOURCE)
458
@interface Vetoable {
459
/**
460
* Generates VetoableChangeSupport for vetoable properties.
461
*/
462
}
463
464
@Target({ElementType.TYPE})
465
@Retention(RetentionPolicy.SOURCE)
466
@interface ListenerList {
467
/**
468
* Generates listener list support.
469
*/
470
}
471
```
472
473
### Category Support
474
475
Annotations for category-based extensions.
476
477
```java { .api }
478
@Target({ElementType.TYPE})
479
@Retention(RetentionPolicy.SOURCE)
480
@interface Category {
481
/**
482
* Class to add methods to.
483
*/
484
Class<?> value();
485
}
486
487
@Target({ElementType.TYPE})
488
@Retention(RetentionPolicy.SOURCE)
489
@interface Mixin {
490
/**
491
* Mixin classes to apply.
492
*/
493
Class<?>[] value();
494
}
495
```
496
497
### Singleton Pattern
498
499
Annotation for generating singleton implementations.
500
501
```java { .api }
502
@Target({ElementType.TYPE})
503
@Retention(RetentionPolicy.SOURCE)
504
@interface Singleton {
505
/**
506
* Property name for singleton instance.
507
*/
508
String property() default "instance";
509
510
/**
511
* Whether to use lazy initialization.
512
*/
513
boolean lazy() default true;
514
515
/**
516
* Whether to make singleton strict (prevent inheritance).
517
*/
518
boolean strict() default true;
519
}
520
```
521
522
## Usage Examples
523
524
### Immutable Classes
525
526
```java
527
import groovy.transform.Immutable;
528
529
@Immutable
530
class Person {
531
String name;
532
int age;
533
List<String> hobbies;
534
}
535
536
// Usage
537
Person person = new Person("John", 30, ["reading", "hiking"]);
538
// person.name = "Jane"; // This would cause a compile error
539
540
// With copyWith
541
@Immutable(copyWith = true)
542
class Address {
543
String street;
544
String city;
545
String country;
546
}
547
548
Address address = new Address("123 Main St", "Anytown", "USA");
549
Address newAddress = address.copyWith(city: "New City");
550
```
551
552
### Builder Pattern
553
554
```java
555
import groovy.transform.builder.Builder;
556
557
@Builder
558
class Car {
559
String make;
560
String model;
561
int year;
562
String color;
563
boolean convertible;
564
}
565
566
// Usage
567
Car car = Car.builder()
568
.make("Toyota")
569
.model("Camry")
570
.year(2023)
571
.color("Blue")
572
.convertible(false)
573
.build();
574
```
575
576
### ToString and Equals Generation
577
578
```java
579
import groovy.transform.ToString;
580
import groovy.transform.EqualsAndHashCode;
581
582
@ToString(includeNames = true, includeFields = true)
583
@EqualsAndHashCode(includeFields = true)
584
class Book {
585
String title;
586
String author;
587
int pages;
588
private String isbn;
589
}
590
591
// Usage
592
Book book1 = new Book(title: "Groovy in Action", author: "Dierk König", pages: 500);
593
Book book2 = new Book(title: "Groovy in Action", author: "Dierk König", pages: 500);
594
595
System.out.println(book1.toString());
596
System.out.println(book1.equals(book2)); // true
597
```
598
599
### Lazy Initialization
600
601
```java
602
import groovy.transform.Lazy;
603
604
class DataProcessor {
605
@Lazy
606
private ExpensiveResource resource = new ExpensiveResource();
607
608
@Lazy(soft = true)
609
private List<String> cache = loadCacheData();
610
611
public void process() {
612
// resource is initialized only when first accessed
613
resource.doWork();
614
}
615
616
private List<String> loadCacheData() {
617
// Expensive operation
618
return Arrays.asList("data1", "data2", "data3");
619
}
620
}
621
```
622
623
### Memoization
624
625
```java
626
import groovy.transform.Memoized;
627
628
class Calculator {
629
@Memoized
630
public long fibonacci(int n) {
631
if (n <= 1) return n;
632
return fibonacci(n - 1) + fibonacci(n - 2);
633
}
634
635
@Memoized(maxCacheSize = 100)
636
public double expensiveCalculation(double x, double y) {
637
// Simulate expensive computation
638
Thread.sleep(1000);
639
return Math.pow(x, y);
640
}
641
}
642
643
// Usage
644
Calculator calc = new Calculator();
645
long fib = calc.fibonacci(40); // Calculated once, then cached
646
```
647
648
### Synchronization
649
650
```java
651
import groovy.transform.Synchronized;
652
import groovy.transform.WithReadLock;
653
import groovy.transform.WithWriteLock;
654
655
class ThreadSafeCounter {
656
private int count = 0;
657
private final Object lock = new Object();
658
659
@Synchronized("lock")
660
public void increment() {
661
count++;
662
}
663
664
@Synchronized("lock")
665
public int getCount() {
666
return count;
667
}
668
}
669
670
class ReadWriteExample {
671
private String data = "";
672
private final java.util.concurrent.locks.ReentrantReadWriteLock rwLock =
673
new java.util.concurrent.locks.ReentrantReadWriteLock();
674
675
@WithReadLock
676
public String getData() {
677
return data;
678
}
679
680
@WithWriteLock
681
public void setData(String newData) {
682
this.data = newData;
683
}
684
}
685
```
686
687
### Bindable Properties
688
689
```java
690
import groovy.transform.Bindable;
691
import java.beans.PropertyChangeListener;
692
import java.beans.PropertyChangeEvent;
693
694
@Bindable
695
class Model {
696
String name;
697
int value;
698
}
699
700
// Usage
701
Model model = new Model();
702
model.addPropertyChangeListener(new PropertyChangeListener() {
703
public void propertyChange(PropertyChangeEvent evt) {
704
System.out.println("Property " + evt.getPropertyName() +
705
" changed from " + evt.getOldValue() +
706
" to " + evt.getNewValue());
707
}
708
});
709
710
model.setName("New Name"); // Fires property change event
711
model.setValue(42); // Fires property change event
712
```
713
714
### Static Compilation
715
716
```java
717
import groovy.transform.CompileStatic;
718
import groovy.transform.TypeChecked;
719
720
@CompileStatic
721
class MathUtils {
722
public static int add(int a, int b) {
723
return a + b; // Statically compiled
724
}
725
726
@TypeChecked
727
public static double divide(double a, double b) {
728
if (b == 0) {
729
throw new IllegalArgumentException("Division by zero");
730
}
731
return a / b; // Type checked but dynamically compiled
732
}
733
}
734
```