Apache Groovy is a powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities, for the Java platform aimed at improving developer productivity thanks to a concise, familiar and easy to learn syntax.
—
Comprehensive meta-programming capabilities for runtime method and property manipulation, dynamic behavior modification, and advanced meta-object protocol features.
Core meta-programming interface for runtime method dispatch and property access.
/**
* Core meta-class interface for runtime method dispatch and property access
*/
interface MetaClass {
/** Invoke method on object with given arguments */
Object invokeMethod(Object object, String methodName, Object[] arguments)
/** Invoke method on class (static method) */
Object invokeStaticMethod(Object object, String methodName, Object[] arguments)
/** Invoke constructor with arguments */
Object invokeConstructor(Object[] arguments)
/** Get property value from object */
Object getProperty(Object object, String property)
/** Set property value on object */
void setProperty(Object object, String property, Object newValue)
/** Get attribute (field) value */
Object getAttribute(Object object, String attribute)
/** Set attribute (field) value */
void setAttribute(Object object, String attribute, Object newValue)
/** Get list of all methods */
List<MetaMethod> getMethods()
/** Get list of methods by name */
List<MetaMethod> getMethods(String name)
/** Get list of all properties */
List<MetaProperty> getProperties()
/** Get specific property by name */
MetaProperty getMetaProperty(String name)
/** Check if method exists */
boolean hasMethod(String name, Object[] arguments)
/** Check if property exists */
boolean hasProperty(Object obj, String name)
/** Get the class this MetaClass represents */
Class getTheClass()
}Default implementation providing comprehensive meta-programming features.
/**
* Default implementation of MetaClass
*/
class MetaClassImpl implements MetaClass, MutableMetaClass {
/** Create MetaClassImpl for given class */
MetaClassImpl(Class theClass)
/** Create with custom class registry */
MetaClassImpl(MetaClassRegistry registry, Class theClass)
/** Add new method to the metaclass */
void addNewMethod(String name, Closure closure)
/** Add new static method */
void addNewStaticMethod(String name, Closure closure)
/** Add new constructor */
void addNewConstructor(Closure closure)
/** Add new property getter */
void addNewProperty(String name, Closure getter)
/** Add new property with getter and setter */
void addNewProperty(String name, Closure getter, Closure setter)
/** Initialize the metaclass */
void initialize()
}Dynamic meta-class that allows runtime addition of methods and properties.
/**
* MetaClass that allows dynamic addition of methods and properties at runtime
*/
class ExpandoMetaClass extends MetaClassImpl {
/** Enable globally for all classes */
static void enableGlobally()
/** Disable globally */
static void disableGlobally()
/** Create ExpandoMetaClass for class */
ExpandoMetaClass(Class clazz)
/** Create with inheritance enabled */
ExpandoMetaClass(Class clazz, boolean register)
/** Define method on metaclass using closure */
void define(Closure definition)
/** Left shift operator for method addition */
ExpandoMetaClass leftShift(Closure closure)
/** Add method dynamically */
void addMethod(String name, Closure closure)
/** Add static method dynamically */
void addStaticMethod(String name, Closure closure)
/** Add constructor dynamically */
void addConstructor(Closure closure)
/** Add property dynamically */
void addProperty(String name, Object value)
}Usage Examples:
// Enable ExpandoMetaClass globally
ExpandoMetaClass.enableGlobally()
// Add method to existing class
String.metaClass.reverse = {
return delegate.reverse()
}
println "hello".reverse() // "olleh"
// Add method with parameters
String.metaClass.multiply = { times ->
return delegate * times
}
println "ha".multiply(3) // "hahaha"
// Add static method
Integer.metaClass.static.random = { max ->
return new Random().nextInt(max)
}
println Integer.random(100) // random number 0-99
// Add property
String.metaClass.getWordCount = {
return delegate.split(/\s+/).size()
}
println "hello world test".wordCount // 3
// Define multiple methods at once
String.metaClass.define {
palindrome = {
return delegate == delegate.reverse()
}
capitalize = {
return delegate.toLowerCase().split(' ').collect {
it.capitalize()
}.join(' ')
}
}
println "racecar".palindrome() // true
println "hello world".capitalize() // "Hello World"
// Add methods to specific instances
def person = new Person()
person.metaClass.greet = {
return "Hello, I'm ${delegate.name}"
}
println person.greet()
// Add constructor
Person.metaClass.constructor = { String name, int age ->
def instance = new Person()
instance.name = name
instance.age = age
return instance
}
def newPerson = new Person("Alice", 30)Representations of methods and properties in the meta-object system.
/**
* Base class for meta-methods
*/
abstract class MetaMethod {
/** Get method name */
String getName()
/** Get return type */
Class getReturnType()
/** Get parameter types */
Class[] getParameterTypes()
/** Check if method is static */
boolean isStatic()
/** Check if method is public */
boolean isPublic()
/** Check if method is private */
boolean isPrivate()
/** Check if method is protected */
boolean isProtected()
/** Invoke the method */
Object invoke(Object object, Object[] arguments)
/** Get declaring class */
Class getDeclaringClass()
}
/**
* Represents a property in the meta-object system
*/
abstract class MetaProperty {
/** Get property name */
String getName()
/** Get property type */
Class getType()
/** Get property value from object */
Object getProperty(Object object)
/** Set property value on object */
void setProperty(Object object, Object newValue)
/** Get declaring class */
Class getDeclaringClass()
}Registry for managing MetaClass instances globally.
/**
* Registry for MetaClass instances
*/
interface MetaClassRegistry {
/** Get MetaClass for given class */
MetaClass getMetaClass(Class theClass)
/** Set MetaClass for given class */
void setMetaClass(Class theClass, MetaClass theMetaClass)
/** Remove MetaClass for given class */
void removeMetaClass(Class theClass)
/** Get MetaClass for given object */
MetaClass getMetaClass(Object obj)
/** Add change event listener */
void addMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener)
/** Remove change event listener */
void removeMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener)
/** Get change listeners */
MetaClassRegistryChangeEventListener[] getMetaClassRegistryChangeEventListeners()
}Usage Examples:
// Get global registry
def registry = GroovySystem.metaClassRegistry
// Get metaclass for a class
def stringMetaClass = registry.getMetaClass(String)
println "String has ${stringMetaClass.methods.size()} methods"
// Set custom metaclass
def customMetaClass = new ExpandoMetaClass(String)
customMetaClass.shout = { delegate.toUpperCase() + "!!!" }
customMetaClass.initialize()
registry.setMetaClass(String, customMetaClass)
println "hello".shout() // "HELLO!!!"
// Listen for metaclass changes
registry.addMetaClassRegistryChangeEventListener { event ->
println "MetaClass changed for ${event.classToUpdate}"
}Temporary method additions using the Category pattern.
/**
* Marks a class as a category for use with `use` blocks
*/
@interface Category {
Class value() default Object.class
}
/**
* Support for category-based method enhancement
*/
class GroovyCategorySupport {
/** Execute closure with category methods available */
static Object use(Class categoryClass, Closure closure)
/** Execute closure with multiple categories */
static Object use(List<Class> categoryClasses, Closure closure)
/** Execute closure with category instance */
static Object use(Object categoryInstance, Closure closure)
}Usage Examples:
import groovy.lang.Category
// Define a category class
@Category(String)
class StringUtils {
static String reverse() {
return this.reverse()
}
static boolean isPalindrome() {
def reversed = this.reverse()
return this.toLowerCase() == reversed.toLowerCase()
}
static String encrypt(String key) {
// Simple encryption example
return this.collect { char ->
(char as int) + (key as int)
}.collect {
it as char
}.join('')
}
}
// Use category methods temporarily
use(StringUtils) {
println "hello".reverse() // "olleh"
println "racecar".isPalindrome() // true
println "secret".encrypt("x") // encrypted string
}
// Outside use block, methods are not available
// "hello".reverse() // Would cause MissingMethodException
// Multiple categories
@Category(Number)
class NumberUtils {
static boolean isEven() {
return this % 2 == 0
}
static boolean isOdd() {
return this % 2 != 0
}
}
use([StringUtils, NumberUtils]) {
println "test".reverse() // String category method
println 42.isEven() // Number category method
}Handle missing method and property calls dynamically.
/**
* Handle missing method calls (typically implemented in classes)
*/
Object methodMissing(String name, Object args) {
// Custom implementation for handling unknown method calls
}
/**
* Handle missing property access (typically implemented in classes)
*/
Object propertyMissing(String name) {
// Custom implementation for handling unknown property access
}
/**
* Handle missing property assignment (typically implemented in classes)
*/
Object propertyMissing(String name, Object value) {
// Custom implementation for handling unknown property assignment
}Usage Examples:
class DynamicBean {
private Map properties = [:]
// Handle missing property gets
def propertyMissing(String name) {
return properties[name]
}
// Handle missing property sets
def propertyMissing(String name, value) {
properties[name] = value
}
// Handle missing method calls
def methodMissing(String name, args) {
if (name.startsWith('get')) {
def propName = name[3..-1].toLowerCase()
return properties[propName]
} else if (name.startsWith('set')) {
def propName = name[3..-1].toLowerCase()
properties[propName] = args[0]
return this
} else {
throw new MissingMethodException(name, this.class, args)
}
}
}
def bean = new DynamicBean()
// Property syntax
bean.name = "Alice"
bean.age = 30
println bean.name // "Alice"
println bean.age // 30
// Method syntax
bean.setEmail("alice@example.com")
println bean.getEmail() // "alice@example.com"
// Method chaining
bean.setName("Bob").setAge(25)
println "${bean.name} is ${bean.age} years old"Intercept method calls for logging, security, or other cross-cutting concerns.
/**
* Interface for method call interception
*/
interface Interceptor {
/** Called before method execution */
Object beforeInvoke(Object object, String methodName, Object[] arguments)
/** Called after method execution */
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result)
/** Check if this interceptor applies to the method */
boolean doInvoke()
}
/**
* Tracing interceptor for debugging
*/
class TracingInterceptor implements Interceptor {
TracingInterceptor()
Object beforeInvoke(Object object, String methodName, Object[] arguments)
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result)
boolean doInvoke()
}Usage Examples:
import groovy.lang.TracingInterceptor
// Add tracing to a class
def tracer = new TracingInterceptor()
def proxy = ProxyMetaClass.getInstance(String)
proxy.interceptor = tracer
proxy.use {
"hello".toUpperCase()
"world".reverse()
}
// Custom interceptor
class LoggingInterceptor implements Interceptor {
boolean doInvoke() { return true }
Object beforeInvoke(Object object, String methodName, Object[] arguments) {
println "Calling ${methodName} on ${object.class.simpleName} with args: ${arguments}"
return null
}
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
println "Method ${methodName} returned: ${result}"
return result
}
}
def logger = new LoggingInterceptor()
def proxyMeta = ProxyMetaClass.getInstance(List)
proxyMeta.interceptor = logger
proxyMeta.use {
def list = [1, 2, 3]
list.add(4)
list.size()
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-groovy--groovy