public class ExpandoMetaClass
extends MetaClassImpl
implements GroovyObject
ExpandoMetaClass is a MetaClass that behaves like an Expando, allowing the addition or replacement of methods, properties and constructors on the fly.
Some examples of usage:
// defines or replaces instance method:
metaClass.myMethod = { args -> }
// defines a new instance method
metaClass.myMethod << { args -> }
// creates multiple overloaded methods of the same name
metaClass.myMethod << { String s -> } << { Integer i -> }
// defines or replaces a static method with the 'static' qualifier
metaClass.'static'.myMethod = { args -> }
// defines a new static method with the 'static' qualifier
metaClass.'static'.myMethod << { args -> }
// defines a new constructor
metaClass.constructor << { String arg -> }
// defines or replaces a constructor
metaClass.constructor = { String arg -> }
// defines a new property with an initial value of "blah"
metaClass.myProperty = "blah"
ExpandoMetaClass also supports a DSL/builder like notation to combine multiple definitions together. So instead of this:
Number.metaClass.multiply = { Amount amount -> amount.times(delegate) }
Number.metaClass.div = { Amount amount -> amount.inverse().times(delegate) }
You can also now do this:
Number.metaClass {
multiply { Amount amount -> amount.times(delegate) }
div { Amount amount -> amount.inverse().times(delegate) }
}
ExpandoMetaClass also supports runtime mixins. While @Mixin allows you to mix in new behavior
to classes you own and are designing, you can not easily mixin anything to types you didn't own, e.g.
from third party libraries or from JDK library classes.
Runtime mixins let you add a mixin on any type at runtime.
interface Vehicle {
String getName()
}
// Category annotation style
@Category(Vehicle) class FlyingAbility {
def fly() { "I'm the ${name} and I fly!" }
}
// traditional category style
class DivingAbility {
static dive(Vehicle self) { "I'm the ${self.name} and I dive!" }
}
// provided by a third-party, so can't augment using Mixin annotation
class JamesBondVehicle implements Vehicle {
String getName() { "James Bond's vehicle" }
}
// Can be added via metaClass, e.g.:
// JamesBondVehicle.metaClass.mixin DivingAbility, FlyingAbility
// Or using shorthand through DGM method on Class
JamesBondVehicle.mixin DivingAbility, FlyingAbility
assert new JamesBondVehicle().fly() ==
"I'm the James Bond's vehicle and I fly!"
assert new JamesBondVehicle().dive() ==
"I'm the James Bond's vehicle and I dive!"
As another example, consider the following class definitions:
class Student {
List schedule = []
def addLecture(String lecture) { schedule << lecture }
}
class Worker {
List schedule = []
def addMeeting(String meeting) { schedule << meeting }
}
We can mimic a form of multiple inheritance as follows:
class CollegeStudent {
static { mixin Student, Worker }
}
new CollegeStudent().with {
addMeeting('Performance review with Boss')
addLecture('Learn about Groovy Mixins')
println schedule
println mixedIn[Student].schedule
println mixedIn[Worker].schedule
}
Which outputs these lines when run:
[Performance review with Boss]
[Learn about Groovy Mixins]
[Performance review with Boss]
Perhaps some explanation is required here. The methods and properties of Student and Worker are
added to CollegeStudent. Worker is added last, so for overlapping methods, its methods will
be used, e.g. when calling schedule, it will be the schedule property (getSchedule method)
from Worker that is used. The schedule property from Student will be shadowed but the mixedIn
notation allows us to get to that too if we need as the last two lines show.
We can also be a little more dynamic and not require the CollegeStudent class to be defined at all, e.g.:
def cs = new Object()
cs.metaClass {
mixin Student, Worker
getSchedule {
mixedIn[Student].schedule + mixedIn[Worker].schedule
}
}
cs.with {
addMeeting('Performance review with Boss')
addLecture('Learn about Groovy Mixins')
println schedule
}
Which outputs this line when run:
[Learn about Groovy Mixins, Performance review with Boss]
As another example, we can also define a no dup queue by mixing in some
Queue and Set functionality as follows:
def ndq = new Object()
ndq.metaClass {
mixin ArrayDeque
mixin HashSet
leftShift = { Object o ->
if (!mixedIn[Set].contains(o)) {
mixedIn[Queue].push(o)
mixedIn[Set].add(o)
}
}
}
ndq << 1
ndq << 2
ndq << 1
assert ndq.size() == 2
As a final example, we sometimes need to pass such mixed in classes or objects
into Java methods which require a given static type but the ExpandoMetaClass mixin approach uses a very dynamic
approach based on duck typing rather than static interface definitions, so doesn't by default
produce objects matching the required static type. Luckily, there is a mixins capability
within ExpandoMetaClass which supports the use of Groovy's common 'as StaticType' notation to produce an object
having the correct static type so that it can be passed to the Java method call in question.
A slightly contrived example illustrating this feature:
class CustomComparator implements Comparator {
int compare(Object a, b) { return a.size() - b.size() }
}
class CustomCloseable implements Closeable {
void close() { println 'Lights out - I am closing' }
}
import static mypackage.IOUtils.closeQuietly
import static java.util.Collections.sort
def o = new Object()
o.metaClass.mixin CustomComparator, CustomCloseable
def items = ['a', 'bbb', 'cc']
sort(items, o as Comparator)
println items // => [a, cc, bbb]
closeQuietly(o as Closeable) // => Lights out - I am closing
Further details
When using the default implementations of MetaClass, methods are only allowed to be added before initialize() is called. In other words you create a new MetaClass, add some methods and then call initialize(). If you attempt to add new methods after initialize() has been called, an error will be thrown. This is to ensure that the MetaClass can operate appropriately in multithreaded environments as it forces you to do all method additions at the beginning, before using the MetaClass.
ExpandoMetaClass differs here from the default in that it allows you to add methods after initialize has been called. This is done by setting the initialize flag internally to false and then add the methods. Since this is not thread safe it has to be done in a synchronized block. The methods to check for modification and initialization are therefore synchronized as well. Any method call done through this metaclass will first check if the it is synchronized. Should this happen during a modification, then the method cannot be selected or called unless the modification is completed.
| Modifiers | Name | Description |
|---|---|---|
protected class |
ExpandoMetaClass.ExpandoMetaConstructor |
Handles the ability to use the left shift operator to append new constructors |
protected class |
ExpandoMetaClass.ExpandoMetaProperty |
Instances of this class are returned when using the << left shift operator. |
| Modifiers | Name | Description |
|---|---|---|
static String |
CONSTRUCTOR |
Pseudo-property used by the expando DSL to define constructors. |
static String |
STATIC_QUALIFIER |
Pseudo-property used by the expando DSL to define static members. |
boolean |
inRegistry |
Indicates whether this expando meta class is registered in the global registry. |
| Fields inherited from class | Fields |
|---|---|
class MetaClassImpl |
EMPTY_ARGUMENTS, INVOKE_METHOD_METHOD, METHOD_MISSING, PROPERTY_MISSING, STATIC_METHOD_MISSING, STATIC_PROPERTY_MISSING, getPropertyMethod, invokeMethodMethod, isGroovyObject, isMap, metaMethodIndex, registry, setPropertyMethod, theCachedClass, theClass |
| Constructor and description |
|---|
ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)Creates an expando meta class with explicit registry behaviour and additional methods. |
ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)Creates an expando meta class bound to a specific registry. |
ExpandoMetaClass(Class theClass)Constructs a new ExpandoMetaClass instance for the given class |
ExpandoMetaClass(Class theClass, MetaMethod[] add)Creates an unregistered expando meta class with additional initial methods. |
ExpandoMetaClass(Class theClass, boolean register)Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically |
ExpandoMetaClass(Class theClass, boolean register, MetaMethod[] add)Creates an expando meta class with optional global registration and additional initial methods. |
ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit)Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically |
| Type Params | Return Type | Name and description |
|---|---|---|
|
public boolean |
addMixinClass(MixinInMetaClass mixin)
|
|
public Object |
castToMixedType(Object obj, Class type)Returns the mixed-in view of an object for the requested mixin type. |
|
protected void |
checkInitalised()checks if the initialisation of the class id complete. This method should be called as a form of assert, it is no way to test if there is still initialisation work to be done. Such logic must be implemented in a different way.
|
|
public CallSite |
createConstructorSite(CallSite site, Object[] args)Create a CallSite |
|
public CallSite |
createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)Creates a call site for current-scope POGO dispatch that honours expando invokeMethod. |
|
public CallSite |
createPogoCallSite(CallSite site, Object[] args)Create a CallSite |
|
public CallSite |
createPojoCallSite(CallSite site, Object receiver, Object[] args)Create a CallSite |
|
public CallSite |
createStaticSite(CallSite site, Object[] args)Create a CallSite |
|
public ExpandoMetaClass |
define(Closure closure)Executes the expando definition DSL against this meta class. |
|
public static void |
disableGlobally()Call to disable the global use of ExpandoMetaClass |
|
public static void |
enableGlobally()Call to enable global use of ExpandoMetaClass within the registry. |
|
public MetaMethod |
findMixinMethod(String methodName, Class[] arguments)Searches for a matching mixin method.
|
|
public List<MetaMethod> |
getExpandoMethods()Returns a list of expando MetaMethod instances added to this ExpandoMetaClass |
|
public Collection<MetaProperty> |
getExpandoProperties()Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass |
|
public Collection |
getExpandoSubclassMethods()Returns the subclass-scoped expando methods registered on this meta class. |
|
public Class |
getJavaClass()
|
|
public MetaClass |
getMetaClass()Returns the metaclass for a given class.
|
|
public MetaProperty |
getMetaProperty(String name)Looks up an existing MetaProperty by name |
|
public List<MetaMethod> |
getMethods()Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods |
|
public List<MetaProperty> |
getProperties()Returns the available properties for this type.
|
|
public Object |
getProperty(String property)Retrieves a property value.
|
|
public Object |
getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass)Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass |
|
public Object |
getProperty(Object object, String name)Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass |
|
public String |
getPropertyForSetter(String setterName)Returns a property name equivalent for the given setter name or null if it is not a getter |
|
protected Object |
getSubclassMetaMethods(String methodName)Returns subclass-scoped meta methods contributed by specialized meta classes.
|
|
public boolean |
hasCustomStaticInvokeMethod()indicates is the metaclass method invocation for static methods is done through a custom invoker object.
|
|
public boolean |
hasMetaMethod(String name, Class[] args)Checks whether a MetaMethod for the given name and arguments exists |
|
public boolean |
hasMetaProperty(String name)Returns true if the MetaClass has the given property |
|
public void |
initialize()Complete the initialisation process. After this method is called no methods should be added to the metaclass. Invocation of methods or access to fields/properties is forbidden unless this method is called. This method should contain any initialisation code, taking a longer time to complete. An example is the creation of the Reflector. It is suggested to synchronize this method. |
|
public Object |
invokeConstructor(Object[] arguments)Invokes a constructor for the given arguments. The MetaClass will attempt to pick the best argument which matches the types of the objects passed within the arguments array
|
|
public Object |
invokeMethod(String name, Object args)Invokes the given method.
|
|
public Object |
invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)Overrides default implementation just in case invokeMethod has been overridden by ExpandoMetaClass |
|
public Object |
invokeStaticMethod(Object object, String methodName, Object[] arguments)Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass |
|
protected boolean |
isInitialized()Checks if the metaclass is initialized. |
|
public boolean |
isModified()Returns whether this MetaClassImpl has been modified. Since MetaClassImpl is not designed for modification this method always returns false
|
|
public boolean |
isSetter(String name, CachedClass[] args)Determines whether the supplied method signature represents a Groovy bean setter. |
|
public static boolean |
isValidExpandoProperty(String property)Checks whether a property name is available for the expando DSL. |
|
protected void |
onGetPropertyFoundInHierarchy(MetaMethod method)Callback invoked when a getProperty implementation is discovered.
|
|
protected void |
onInvokeMethodFoundInHierarchy(MetaMethod method)Callback invoked when an invokeMethod implementation is discovered.
|
|
protected void |
onSetPropertyFoundInHierarchy(MetaMethod method)Callback invoked when a setProperty implementation is discovered.
|
|
protected void |
onSuperMethodFoundInHierarchy(MetaMethod method)Callback invoked when a super-class method is discovered during initialization.
|
|
protected void |
onSuperPropertyFoundInHierarchy(MetaBeanProperty property)Callback invoked when a super-class bean property is discovered during initialization.
|
|
protected void |
performOperationOnMetaClass(Runnable runner)Performs a mutating expando operation while coordinating initialization state and locks. |
|
public void |
refreshInheritedMethods(Set modifiedSuperExpandos)Called from ExpandoMetaClassCreationHandle in the registry if it exists to set up inheritance handling |
|
public void |
registerBeanProperty(String property, Object newValue)Registers a new bean property. |
|
public void |
registerInstanceMethod(String name, Closure closure)Registers a new instance method. |
|
public void |
registerInstanceMethod(MetaMethod metaMethod)Registers a new instance method. |
|
protected void |
registerStaticMethod(String name, Closure callable)Registers a new static expando method using the closure's declared parameter types. |
|
protected void |
registerStaticMethod(String name, Closure callable, Class[] paramTypes)Registers a new static method for the given method name and closure on this MetaClass |
|
public void |
registerSubclassInstanceMethod(String name, Class klazz, Closure closure)Registers an instance method contribution that only applies to the supplied subclass. |
|
public void |
registerSubclassInstanceMethod(MetaMethod metaMethod)Registers a subclass-scoped meta method. |
|
public MetaMethod |
retrieveConstructor(Object[] args)This is a helper method which is used only by indy. It is for internal use.
|
|
protected void |
setInitialized(boolean b)Updates the initialization flag for this meta class.
|
|
public void |
setMetaClass(MetaClass metaClass)Allows the MetaClass to be replaced with a derived implementation.
|
|
public void |
setProperty(String property, Object newValue)Sets the given property to the new value.
|
|
public void |
setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) |
Pseudo-property used by the expando DSL to define constructors.
Pseudo-property used by the expando DSL to define static members.
Indicates whether this expando meta class is registered in the global registry.
Creates an expando meta class with explicit registry behaviour and additional methods.
theClass - the enhanced classregister - whether the meta class should be registered globallyallowChangesAfterInit - whether mutation is allowed after initializationadd - additional meta methods to seed the meta class withCreates an expando meta class bound to a specific registry.
registry - the meta class registry to work withtheClass - the enhanced classregister - whether the meta class should be registered globallyallowChangesAfterInit - whether mutation is allowed after initializationadd - additional meta methods to seed the meta class withConstructs a new ExpandoMetaClass instance for the given class
theClass - The class that the MetaClass applies toCreates an unregistered expando meta class with additional initial methods.
theClass - the enhanced classadd - additional meta methods to seed the meta class withConstructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
theClass - The class that the MetaClass applies toregister - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will affect all instances if changedCreates an expando meta class with optional global registration and additional initial methods.
theClass - the enhanced classregister - whether the meta class should be registered globallyadd - additional meta methods to seed the meta class withConstructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
theClass - The class that the MetaClass applies toregister - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will affect all instances if changedallowChangesAfterInit - Should the metaclass be modifiable after initialization. Default is false.
Returns the mixed-in view of an object for the requested mixin type.
obj - the receiver objecttype - the requested mixin typenull if the type is not mixed inchecks if the initialisation of the class id complete. This method should be called as a form of assert, it is no way to test if there is still initialisation work to be done. Such logic must be implemented in a different way.
Creates a call site for current-scope POGO dispatch that honours expando invokeMethod.
site - the original call sitesender - the calling classname - the method name being invokedargs - the invocation argumentsExecutes the expando definition DSL against this meta class.
closure - the definition closureCall to disable the global use of ExpandoMetaClass
Call to enable global use of ExpandoMetaClass within the registry. This has the advantage that inheritance will function correctly and metaclass modifications will also apply to existing objects, but has a higher memory usage on the JVM than normal Groovy
Searches for a matching mixin method.
methodName - the method namearguments - the parameter typesnull if none is foundReturns a list of expando MetaMethod instances added to this ExpandoMetaClass
Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
Returns the subclass-scoped expando methods registered on this meta class.
Returns the metaclass for a given class.
Looks up an existing MetaProperty by name
name - The name of the MetaPropertyOverrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
Returns the available properties for this type.
MetaProperty objectsRetrieves a property value.
propertyName - the name of the property of interestOverrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
Returns a property name equivalent for the given setter name or null if it is not a getter
setterName - The setter nameReturns subclass-scoped meta methods contributed by specialized meta classes.
methodName - the method namenull if none are registeredindicates is the metaclass method invocation for static methods is done through a custom invoker object.
Checks whether a MetaMethod for the given name and arguments exists
name - The name of the MetaMethodargs - The arguments to the meta methodReturns true if the MetaClass has the given property
name - The name of the MetaPropertyComplete the initialisation process. After this method is called no methods should be added to the metaclass. Invocation of methods or access to fields/properties is forbidden unless this method is called. This method should contain any initialisation code, taking a longer time to complete. An example is the creation of the Reflector. It is suggested to synchronize this method.
Invokes a constructor for the given arguments. The MetaClass will attempt to pick the best argument which matches the types of the objects passed within the arguments array
arguments - The arguments to the constructorInvokes the given method.
name - the name of the method to callargs - the arguments to use for the method callOverrides default implementation just in case invokeMethod has been overridden by ExpandoMetaClass
Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
Checks if the metaclass is initialized.
Returns whether this MetaClassImpl has been modified. Since MetaClassImpl is not designed for modification this method always returns false
Determines whether the supplied method signature represents a Groovy bean setter.
name - the candidate method nameargs - the candidate parameter typestrue if the signature represents a setterChecks whether a property name is available for the expando DSL.
property - the property name to testtrue if the name can be used by the expando DSL Callback invoked when a getProperty implementation is discovered.
method - the discovered handler Callback invoked when an invokeMethod implementation is discovered.
method - the discovered handler Callback invoked when a setProperty implementation is discovered.
method - the discovered handlerCallback invoked when a super-class method is discovered during initialization.
method - the inherited methodCallback invoked when a super-class bean property is discovered during initialization.
property - the inherited propertyPerforms a mutating expando operation while coordinating initialization state and locks.
runner - the operation to executeCalled from ExpandoMetaClassCreationHandle in the registry if it exists to set up inheritance handling
modifiedSuperExpandos - A list of modified super ExpandoMetaClassRegisters a new bean property.
property - the property namenewValue - the properties initial valueRegisters a new instance method.
name - the method name or "constructor" for a constructorclosure - the implementationRegisters a new instance method.
Registers a new static expando method using the closure's declared parameter types.
name - the method namecallable - the implementation closureRegisters a new static method for the given method name and closure on this MetaClass
name - The method namecallable - The callable ClosureRegisters an instance method contribution that only applies to the supplied subclass.
name - the method nameklazz - the subclass receiving the methodclosure - the implementation closureRegisters a subclass-scoped meta method.
metaMethod - the method to registerThis is a helper method which is used only by indy. It is for internal use.
Updates the initialization flag for this meta class.
initialized - the new initialization stateAllows the MetaClass to be replaced with a derived implementation.
metaClass - the new metaclassSets the given property to the new value.
propertyName - the name of the property of interestnewValue - the new value for the propertyCopyright © 2003-2026 The Apache Software Foundation. All rights reserved.