0
# Meta-Programming
1
2
Comprehensive meta-programming capabilities for runtime method and property manipulation, dynamic behavior modification, and advanced meta-object protocol features.
3
4
## Capabilities
5
6
### MetaClass System
7
8
Core meta-programming interface for runtime method dispatch and property access.
9
10
```groovy { .api }
11
/**
12
* Core meta-class interface for runtime method dispatch and property access
13
*/
14
interface MetaClass {
15
/** Invoke method on object with given arguments */
16
Object invokeMethod(Object object, String methodName, Object[] arguments)
17
18
/** Invoke method on class (static method) */
19
Object invokeStaticMethod(Object object, String methodName, Object[] arguments)
20
21
/** Invoke constructor with arguments */
22
Object invokeConstructor(Object[] arguments)
23
24
/** Get property value from object */
25
Object getProperty(Object object, String property)
26
27
/** Set property value on object */
28
void setProperty(Object object, String property, Object newValue)
29
30
/** Get attribute (field) value */
31
Object getAttribute(Object object, String attribute)
32
33
/** Set attribute (field) value */
34
void setAttribute(Object object, String attribute, Object newValue)
35
36
/** Get list of all methods */
37
List<MetaMethod> getMethods()
38
39
/** Get list of methods by name */
40
List<MetaMethod> getMethods(String name)
41
42
/** Get list of all properties */
43
List<MetaProperty> getProperties()
44
45
/** Get specific property by name */
46
MetaProperty getMetaProperty(String name)
47
48
/** Check if method exists */
49
boolean hasMethod(String name, Object[] arguments)
50
51
/** Check if property exists */
52
boolean hasProperty(Object obj, String name)
53
54
/** Get the class this MetaClass represents */
55
Class getTheClass()
56
}
57
```
58
59
### MetaClass Implementation
60
61
Default implementation providing comprehensive meta-programming features.
62
63
```groovy { .api }
64
/**
65
* Default implementation of MetaClass
66
*/
67
class MetaClassImpl implements MetaClass, MutableMetaClass {
68
/** Create MetaClassImpl for given class */
69
MetaClassImpl(Class theClass)
70
71
/** Create with custom class registry */
72
MetaClassImpl(MetaClassRegistry registry, Class theClass)
73
74
/** Add new method to the metaclass */
75
void addNewMethod(String name, Closure closure)
76
77
/** Add new static method */
78
void addNewStaticMethod(String name, Closure closure)
79
80
/** Add new constructor */
81
void addNewConstructor(Closure closure)
82
83
/** Add new property getter */
84
void addNewProperty(String name, Closure getter)
85
86
/** Add new property with getter and setter */
87
void addNewProperty(String name, Closure getter, Closure setter)
88
89
/** Initialize the metaclass */
90
void initialize()
91
}
92
```
93
94
### ExpandoMetaClass
95
96
Dynamic meta-class that allows runtime addition of methods and properties.
97
98
```groovy { .api }
99
/**
100
* MetaClass that allows dynamic addition of methods and properties at runtime
101
*/
102
class ExpandoMetaClass extends MetaClassImpl {
103
/** Enable globally for all classes */
104
static void enableGlobally()
105
106
/** Disable globally */
107
static void disableGlobally()
108
109
/** Create ExpandoMetaClass for class */
110
ExpandoMetaClass(Class clazz)
111
112
/** Create with inheritance enabled */
113
ExpandoMetaClass(Class clazz, boolean register)
114
115
/** Define method on metaclass using closure */
116
void define(Closure definition)
117
118
/** Left shift operator for method addition */
119
ExpandoMetaClass leftShift(Closure closure)
120
121
/** Add method dynamically */
122
void addMethod(String name, Closure closure)
123
124
/** Add static method dynamically */
125
void addStaticMethod(String name, Closure closure)
126
127
/** Add constructor dynamically */
128
void addConstructor(Closure closure)
129
130
/** Add property dynamically */
131
void addProperty(String name, Object value)
132
}
133
```
134
135
**Usage Examples:**
136
137
```groovy
138
// Enable ExpandoMetaClass globally
139
ExpandoMetaClass.enableGlobally()
140
141
// Add method to existing class
142
String.metaClass.reverse = {
143
return delegate.reverse()
144
}
145
146
println "hello".reverse() // "olleh"
147
148
// Add method with parameters
149
String.metaClass.multiply = { times ->
150
return delegate * times
151
}
152
153
println "ha".multiply(3) // "hahaha"
154
155
// Add static method
156
Integer.metaClass.static.random = { max ->
157
return new Random().nextInt(max)
158
}
159
160
println Integer.random(100) // random number 0-99
161
162
// Add property
163
String.metaClass.getWordCount = {
164
return delegate.split(/\s+/).size()
165
}
166
167
println "hello world test".wordCount // 3
168
169
// Define multiple methods at once
170
String.metaClass.define {
171
palindrome = {
172
return delegate == delegate.reverse()
173
}
174
175
capitalize = {
176
return delegate.toLowerCase().split(' ').collect {
177
it.capitalize()
178
}.join(' ')
179
}
180
}
181
182
println "racecar".palindrome() // true
183
println "hello world".capitalize() // "Hello World"
184
185
// Add methods to specific instances
186
def person = new Person()
187
person.metaClass.greet = {
188
return "Hello, I'm ${delegate.name}"
189
}
190
println person.greet()
191
192
// Add constructor
193
Person.metaClass.constructor = { String name, int age ->
194
def instance = new Person()
195
instance.name = name
196
instance.age = age
197
return instance
198
}
199
200
def newPerson = new Person("Alice", 30)
201
```
202
203
### MetaMethod and MetaProperty
204
205
Representations of methods and properties in the meta-object system.
206
207
```groovy { .api }
208
/**
209
* Base class for meta-methods
210
*/
211
abstract class MetaMethod {
212
/** Get method name */
213
String getName()
214
215
/** Get return type */
216
Class getReturnType()
217
218
/** Get parameter types */
219
Class[] getParameterTypes()
220
221
/** Check if method is static */
222
boolean isStatic()
223
224
/** Check if method is public */
225
boolean isPublic()
226
227
/** Check if method is private */
228
boolean isPrivate()
229
230
/** Check if method is protected */
231
boolean isProtected()
232
233
/** Invoke the method */
234
Object invoke(Object object, Object[] arguments)
235
236
/** Get declaring class */
237
Class getDeclaringClass()
238
}
239
240
/**
241
* Represents a property in the meta-object system
242
*/
243
abstract class MetaProperty {
244
/** Get property name */
245
String getName()
246
247
/** Get property type */
248
Class getType()
249
250
/** Get property value from object */
251
Object getProperty(Object object)
252
253
/** Set property value on object */
254
void setProperty(Object object, Object newValue)
255
256
/** Get declaring class */
257
Class getDeclaringClass()
258
}
259
```
260
261
### MetaClassRegistry
262
263
Registry for managing MetaClass instances globally.
264
265
```groovy { .api }
266
/**
267
* Registry for MetaClass instances
268
*/
269
interface MetaClassRegistry {
270
/** Get MetaClass for given class */
271
MetaClass getMetaClass(Class theClass)
272
273
/** Set MetaClass for given class */
274
void setMetaClass(Class theClass, MetaClass theMetaClass)
275
276
/** Remove MetaClass for given class */
277
void removeMetaClass(Class theClass)
278
279
/** Get MetaClass for given object */
280
MetaClass getMetaClass(Object obj)
281
282
/** Add change event listener */
283
void addMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener)
284
285
/** Remove change event listener */
286
void removeMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener)
287
288
/** Get change listeners */
289
MetaClassRegistryChangeEventListener[] getMetaClassRegistryChangeEventListeners()
290
}
291
```
292
293
**Usage Examples:**
294
295
```groovy
296
// Get global registry
297
def registry = GroovySystem.metaClassRegistry
298
299
// Get metaclass for a class
300
def stringMetaClass = registry.getMetaClass(String)
301
println "String has ${stringMetaClass.methods.size()} methods"
302
303
// Set custom metaclass
304
def customMetaClass = new ExpandoMetaClass(String)
305
customMetaClass.shout = { delegate.toUpperCase() + "!!!" }
306
customMetaClass.initialize()
307
308
registry.setMetaClass(String, customMetaClass)
309
println "hello".shout() // "HELLO!!!"
310
311
// Listen for metaclass changes
312
registry.addMetaClassRegistryChangeEventListener { event ->
313
println "MetaClass changed for ${event.classToUpdate}"
314
}
315
```
316
317
### Categories
318
319
Temporary method additions using the Category pattern.
320
321
```groovy { .api }
322
/**
323
* Marks a class as a category for use with `use` blocks
324
*/
325
@interface Category {
326
Class value() default Object.class
327
}
328
329
/**
330
* Support for category-based method enhancement
331
*/
332
class GroovyCategorySupport {
333
/** Execute closure with category methods available */
334
static Object use(Class categoryClass, Closure closure)
335
336
/** Execute closure with multiple categories */
337
static Object use(List<Class> categoryClasses, Closure closure)
338
339
/** Execute closure with category instance */
340
static Object use(Object categoryInstance, Closure closure)
341
}
342
```
343
344
**Usage Examples:**
345
346
```groovy
347
import groovy.lang.Category
348
349
// Define a category class
350
@Category(String)
351
class StringUtils {
352
static String reverse() {
353
return this.reverse()
354
}
355
356
static boolean isPalindrome() {
357
def reversed = this.reverse()
358
return this.toLowerCase() == reversed.toLowerCase()
359
}
360
361
static String encrypt(String key) {
362
// Simple encryption example
363
return this.collect { char ->
364
(char as int) + (key as int)
365
}.collect {
366
it as char
367
}.join('')
368
}
369
}
370
371
// Use category methods temporarily
372
use(StringUtils) {
373
println "hello".reverse() // "olleh"
374
println "racecar".isPalindrome() // true
375
println "secret".encrypt("x") // encrypted string
376
}
377
378
// Outside use block, methods are not available
379
// "hello".reverse() // Would cause MissingMethodException
380
381
// Multiple categories
382
@Category(Number)
383
class NumberUtils {
384
static boolean isEven() {
385
return this % 2 == 0
386
}
387
388
static boolean isOdd() {
389
return this % 2 != 0
390
}
391
}
392
393
use([StringUtils, NumberUtils]) {
394
println "test".reverse() // String category method
395
println 42.isEven() // Number category method
396
}
397
```
398
399
### Method Missing and Property Missing
400
401
Handle missing method and property calls dynamically.
402
403
```groovy { .api }
404
/**
405
* Handle missing method calls (typically implemented in classes)
406
*/
407
Object methodMissing(String name, Object args) {
408
// Custom implementation for handling unknown method calls
409
}
410
411
/**
412
* Handle missing property access (typically implemented in classes)
413
*/
414
Object propertyMissing(String name) {
415
// Custom implementation for handling unknown property access
416
}
417
418
/**
419
* Handle missing property assignment (typically implemented in classes)
420
*/
421
Object propertyMissing(String name, Object value) {
422
// Custom implementation for handling unknown property assignment
423
}
424
```
425
426
**Usage Examples:**
427
428
```groovy
429
class DynamicBean {
430
private Map properties = [:]
431
432
// Handle missing property gets
433
def propertyMissing(String name) {
434
return properties[name]
435
}
436
437
// Handle missing property sets
438
def propertyMissing(String name, value) {
439
properties[name] = value
440
}
441
442
// Handle missing method calls
443
def methodMissing(String name, args) {
444
if (name.startsWith('get')) {
445
def propName = name[3..-1].toLowerCase()
446
return properties[propName]
447
} else if (name.startsWith('set')) {
448
def propName = name[3..-1].toLowerCase()
449
properties[propName] = args[0]
450
return this
451
} else {
452
throw new MissingMethodException(name, this.class, args)
453
}
454
}
455
}
456
457
def bean = new DynamicBean()
458
459
// Property syntax
460
bean.name = "Alice"
461
bean.age = 30
462
println bean.name // "Alice"
463
println bean.age // 30
464
465
// Method syntax
466
bean.setEmail("alice@example.com")
467
println bean.getEmail() // "alice@example.com"
468
469
// Method chaining
470
bean.setName("Bob").setAge(25)
471
println "${bean.name} is ${bean.age} years old"
472
```
473
474
### Interceptors
475
476
Intercept method calls for logging, security, or other cross-cutting concerns.
477
478
```groovy { .api }
479
/**
480
* Interface for method call interception
481
*/
482
interface Interceptor {
483
/** Called before method execution */
484
Object beforeInvoke(Object object, String methodName, Object[] arguments)
485
486
/** Called after method execution */
487
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result)
488
489
/** Check if this interceptor applies to the method */
490
boolean doInvoke()
491
}
492
493
/**
494
* Tracing interceptor for debugging
495
*/
496
class TracingInterceptor implements Interceptor {
497
TracingInterceptor()
498
499
Object beforeInvoke(Object object, String methodName, Object[] arguments)
500
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result)
501
boolean doInvoke()
502
}
503
```
504
505
**Usage Examples:**
506
507
```groovy
508
import groovy.lang.TracingInterceptor
509
510
// Add tracing to a class
511
def tracer = new TracingInterceptor()
512
def proxy = ProxyMetaClass.getInstance(String)
513
proxy.interceptor = tracer
514
proxy.use {
515
"hello".toUpperCase()
516
"world".reverse()
517
}
518
519
// Custom interceptor
520
class LoggingInterceptor implements Interceptor {
521
boolean doInvoke() { return true }
522
523
Object beforeInvoke(Object object, String methodName, Object[] arguments) {
524
println "Calling ${methodName} on ${object.class.simpleName} with args: ${arguments}"
525
return null
526
}
527
528
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
529
println "Method ${methodName} returned: ${result}"
530
return result
531
}
532
}
533
534
def logger = new LoggingInterceptor()
535
def proxyMeta = ProxyMetaClass.getInstance(List)
536
proxyMeta.interceptor = logger
537
proxyMeta.use {
538
def list = [1, 2, 3]
539
list.add(4)
540
list.size()
541
}
542
```