public class ExpandoMetaClass extends MetaClassImpl implements GroovyObject
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 multi-threaded 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 meta class 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.
| Modifier and Type | Class and 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.
|
MetaClassImpl.Index, MetaClassImpl.MetaConstructor| Modifier and Type | Field and Description |
|---|---|
static String |
CONSTRUCTOR |
boolean |
inRegistry |
static String |
STATIC_QUALIFIER |
EMPTY_ARGUMENTS, getPropertyMethod, INVOKE_METHOD_METHOD, invokeMethodMethod, isGroovyObject, isMap, metaMethodIndex, METHOD_MISSING, PROPERTY_MISSING, registry, setPropertyMethod, STATIC_METHOD_MISSING, STATIC_PROPERTY_MISSING, theCachedClass, theClass| Constructor and Description |
|---|
ExpandoMetaClass(Class theClass)
Constructs a new ExpandoMetaClass instance for the given class
|
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,
boolean allowChangesAfterInit)
Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass
in the MetaClassRegistry automatically
|
ExpandoMetaClass(Class theClass,
boolean register,
boolean allowChangesAfterInit,
MetaMethod[] add) |
ExpandoMetaClass(Class theClass,
boolean register,
MetaMethod[] add) |
ExpandoMetaClass(Class theClass,
MetaMethod[] add) |
ExpandoMetaClass(MetaClassRegistry registry,
Class theClass,
boolean register,
boolean allowChangesAfterInit,
MetaMethod[] add) |
| Modifier and Type | Method and Description |
|---|---|
void |
addMixinClass(MixinInMetaClass mixin) |
Object |
castToMixedType(Object obj,
Class type) |
protected void |
checkInitalised()
checks if the initialisation of the class id complete.
|
CallSite |
createConstructorSite(CallSite site,
Object[] args)
Create a CallSite
|
CallSite |
createPogoCallCurrentSite(CallSite site,
Class sender,
String name,
Object[] args) |
CallSite |
createPogoCallSite(CallSite site,
Object[] args)
Create a CallSite
|
CallSite |
createPojoCallSite(CallSite site,
Object receiver,
Object[] args)
Create a CallSite
|
CallSite |
createStaticSite(CallSite site,
Object[] args)
Create a CallSite
|
ExpandoMetaClass |
define(Closure closure) |
static void |
disableGlobally()
Call to disable the global use of ExpandoMetaClass
|
static void |
enableGlobally()
Call to enable global use of ExpandoMetaClass within the registry.
|
MetaMethod |
findMixinMethod(String methodName,
Class[] arguments) |
List<MetaMethod> |
getExpandoMethods()
Returns a list of expando MetaMethod instances added to this ExpandoMetaClass
|
Collection<MetaProperty> |
getExpandoProperties()
Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
|
Collection |
getExpandoSubclassMethods() |
Class |
getJavaClass() |
MetaClass |
getMetaClass()
Returns the metaclass for a given class.
|
MetaProperty |
getMetaProperty(String name)
Looks up an existing MetaProperty by name
|
List<MetaMethod> |
getMethods()
Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
|
List<MetaProperty> |
getProperties()
Get all the properties defined for this type
|
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
|
Object |
getProperty(Object object,
String name)
Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
|
Object |
getProperty(String property)
Retrieves a property value.
|
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) |
boolean |
hasCustomStaticInvokeMethod()
indicates is the meta class method invocation for static methods is done
through a custom invoker object.
|
boolean |
hasMetaMethod(String name,
Class[] args)
Checks whether a MetaMethod for the given name and arguments exists
|
boolean |
hasMetaProperty(String name)
Returns true if the MetaClass has the given property
|
void |
initialize()
Complete the initialisation process.
|
Object |
invokeConstructor(Object[] arguments)
Invokes a constructor for the given arguments.
|
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
|
Object |
invokeMethod(String name,
Object args)
Invokes the given method.
|
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 meta class is initialized.
|
boolean |
isModified()
Returns whether this MetaClassImpl has been modified.
|
boolean |
isSetter(String name,
CachedClass[] args) |
static boolean |
isValidExpandoProperty(String property) |
protected void |
onGetPropertyFoundInHierarchy(MetaMethod method) |
protected void |
onInvokeMethodFoundInHierarchy(MetaMethod method) |
protected void |
onSetPropertyFoundInHierarchy(MetaMethod method) |
protected void |
onSuperMethodFoundInHierarchy(MetaMethod method) |
protected void |
onSuperPropertyFoundInHierarchy(MetaBeanProperty property) |
protected void |
performOperationOnMetaClass(groovy.lang.ExpandoMetaClass.Callable c) |
void |
refreshInheritedMethods(Set modifiedSuperExpandos)
Called from ExpandoMetaClassCreationHandle in the registry if it exists to
set up inheritance handling
|
void |
registerBeanProperty(String property,
Object newValue)
Registers a new bean property
|
void |
registerInstanceMethod(MetaMethod metaMethod)
Registers a new instance method for the given method name and closure on this MetaClass
|
void |
registerInstanceMethod(String name,
Closure closure) |
protected void |
registerStaticMethod(String name,
Closure callable) |
protected void |
registerStaticMethod(String name,
Closure callable,
Class[] paramTypes)
Registers a new static method for the given method name and closure on this MetaClass
|
void |
registerSubclassInstanceMethod(MetaMethod metaMethod) |
void |
registerSubclassInstanceMethod(String name,
Class klazz,
Closure closure) |
MetaMethod |
retrieveConstructor(Object[] args)
This is a helper method added in Groovy 2.1.0, which is used only by indy.
|
protected void |
setInitialized(boolean b) |
void |
setMetaClass(MetaClass metaClass)
Allows the MetaClass to be replaced with a derived implementation.
|
void |
setProperty(Class sender,
Object object,
String name,
Object newValue,
boolean useSuper,
boolean fromInsideClass)
Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass
|
void |
setProperty(String property,
Object newValue)
Sets the given property to the new value.
|
addMetaBeanProperty, addMetaMethod, addMetaMethodToIndex, addNewInstanceMethod, addNewStaticMethod, applyPropertyDescriptors, checkIfGroovyObjectMethod, chooseMethod, clearInvocationCaches, createPogoCallCurrentSite, dropMethodCache, dropStaticMethodCache, findMethodInClassHierarchy, findOwnMethod, findPropertyInClassHierarchy, getAdditionalMetaMethods, getAttribute, getAttribute, getAttribute, getClassInfo, getClassNode, getEffectiveGetMetaProperty, getMetaMethod, getMetaMethods, getMethodWithCaching, getMethodWithoutCaching, getRegistry, getStaticMetaMethod, getSuperClasses, getTheCachedClass, getTheClass, getVersion, hasCustomInvokeMethod, hasProperty, incVersion, invokeMethod, invokeMethod, invokeMissingMethod, invokeMissingProperty, invokeStaticMissingProperty, isGroovyObject, onMixinMethodFound, pickMethod, respondsTo, respondsTo, retrieveConstructor, retrieveStaticMethod, selectConstructorAndTransformArguments, setAttribute, setAttribute, setProperties, setProperty, toStringpublic static final String STATIC_QUALIFIER
public static final String CONSTRUCTOR
public boolean inRegistry
public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)
public ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)
public ExpandoMetaClass(Class theClass)
theClass - The class that the MetaClass applies topublic ExpandoMetaClass(Class theClass, MetaMethod[] add)
public ExpandoMetaClass(Class theClass, boolean register)
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 effect all instances if changedpublic ExpandoMetaClass(Class theClass, boolean register, MetaMethod[] add)
public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit)
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 effect all instances if changedallowChangesAfterInit - Should the meta class be modifiable after initialization. Default is false.public Collection getExpandoSubclassMethods()
public MetaMethod findMixinMethod(String methodName, Class[] arguments)
findMixinMethod in class MetaClassImplprotected void onInvokeMethodFoundInHierarchy(MetaMethod method)
onInvokeMethodFoundInHierarchy in class MetaClassImplprotected void onSuperMethodFoundInHierarchy(MetaMethod method)
onSuperMethodFoundInHierarchy in class MetaClassImplprotected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property)
onSuperPropertyFoundInHierarchy in class MetaClassImplprotected void onSetPropertyFoundInHierarchy(MetaMethod method)
onSetPropertyFoundInHierarchy in class MetaClassImplprotected void onGetPropertyFoundInHierarchy(MetaMethod method)
onGetPropertyFoundInHierarchy in class MetaClassImplpublic boolean isModified()
MetaClassImplisModified in interface MutableMetaClassisModified in class MetaClassImplpublic void registerSubclassInstanceMethod(String name, Class klazz, Closure closure)
public void registerSubclassInstanceMethod(MetaMethod metaMethod)
public void addMixinClass(MixinInMetaClass mixin)
public static void enableGlobally()
public static void disableGlobally()
public void initialize()
MetaClassImplinitialize in interface MetaClassinitialize in class MetaClassImplprotected boolean isInitialized()
isInitialized in class MetaClassImplMetaClassImpl.isInitialized()protected void setInitialized(boolean b)
public Object invokeConstructor(Object[] arguments)
MetaObjectProtocolinvokeConstructor in interface MetaObjectProtocolinvokeConstructor in class MetaClassImplarguments - The arguments to the constructorpublic MetaClass getMetaClass()
GroovyObjectgetMetaClass in interface GroovyObjectpublic Object getProperty(String property)
GroovyObjectgetProperty in interface GroovyObjectproperty - the name of the property of interestpublic static boolean isValidExpandoProperty(String property)
public Object invokeMethod(String name, Object args)
GroovyObjectinvokeMethod in interface GroovyObjectname - the name of the method to callargs - the arguments to use for the method callpublic void setMetaClass(MetaClass metaClass)
GroovyObjectsetMetaClass in interface GroovyObjectmetaClass - the new metaclasspublic void setProperty(String property, Object newValue)
GroovyObjectsetProperty in interface GroovyObjectproperty - the name of the property of interestnewValue - the new value for the propertypublic ExpandoMetaClass define(Closure closure)
protected void performOperationOnMetaClass(groovy.lang.ExpandoMetaClass.Callable c)
protected void checkInitalised()
MetaClassImplcheckInitalised in class MetaClassImplpublic void registerBeanProperty(String property, Object newValue)
property - The property namenewValue - The properties initial valuepublic void registerInstanceMethod(MetaMethod metaMethod)
metaMethod - public List<MetaMethod> getMethods()
getMethods in interface MetaClassgetMethods in interface MetaObjectProtocolgetMethods in class MetaClassImplMetaObjectProtocol.getMethods()public List<MetaProperty> getProperties()
MetaClassImplgetProperties in interface MetaClassgetProperties in interface MetaObjectProtocolgetProperties in class MetaClassImplMetaPropertyprotected void registerStaticMethod(String name, Closure callable, Class[] paramTypes)
name - The method namecallable - The callable Closureprotected Object getSubclassMetaMethods(String methodName)
getSubclassMetaMethods in class MetaClassImplpublic Class getJavaClass()
public void refreshInheritedMethods(Set modifiedSuperExpandos)
modifiedSuperExpandos - A list of modified super ExpandoMetaClasspublic List<MetaMethod> getExpandoMethods()
public Collection<MetaProperty> getExpandoProperties()
public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)
invokeMethod in interface MetaClassinvokeMethod in class MetaClassImplsender - The java.lang.Class instance that invoked the methodobject - The object which the method was invoked onmethodName - The name of the methodoriginalArguments - The arguments to the methodisCallToSuper - Whether the method is a call to a super class methodfromInsideClass - Whether the call was invoked from the inside or the outside of the classMetaClassImpl.invokeMethod(Class, Object, String, Object[], boolean, boolean)public Object invokeStaticMethod(Object object, String methodName, Object[] arguments)
invokeStaticMethod in interface MetaObjectProtocolinvokeStaticMethod in class MetaClassImplobject - An instance of the class returned by the getTheClass() method or the class itselfmethodName - The name of the methodarguments - The arguments to the methodMetaClassImpl.invokeStaticMethod(Object, String, Object[])public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass)
getProperty in interface MetaClassgetProperty in class MetaClassImplsender - The java.lang.Class instance that requested the propertyobject - The Object which the property is being retrieved fromname - The name of the propertyuseSuper - Whether the call is to a super class propertyfromInsideClass - ??MetaClassImpl.getProperty(Class, Object, String, boolean, boolean)public Object getProperty(Object object, String name)
getProperty in interface MetaObjectProtocolgetProperty in class MetaClassImplobject - The Object which the property is being retrieved fromname - The name of the propertyMetaClassImpl.getProperty(Object, String)public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass)
setProperty in interface MetaClasssetProperty in class MetaClassImplsender - The java.lang.Class instance that is mutating the propertyobject - The Object which the property is being set onname - The name of the propertynewValue - The new value of the property to setuseSuper - Whether the call is to a super class propertyfromInsideClass - Whether the call was invoked from the inside or the outside of the class.MetaClassImpl.setProperty(Class, Object, String, Object, boolean, boolean)public MetaProperty getMetaProperty(String name)
getMetaProperty in interface MetaObjectProtocolgetMetaProperty in class MetaClassImplname - The name of the MetaPropertyMetaObjectProtocol.getMetaProperty(String)public boolean hasMetaProperty(String name)
name - The name of the MetaPropertypublic boolean hasMetaMethod(String name, Class[] args)
name - The name of the MetaMethodargs - The arguments to the meta methodpublic String getPropertyForSetter(String setterName)
setterName - The setter namepublic boolean isSetter(String name, CachedClass[] args)
public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args)
MetaClassImplcreatePojoCallSite in class MetaClassImplpublic CallSite createStaticSite(CallSite site, Object[] args)
MetaClassImplcreateStaticSite in class MetaClassImplpublic boolean hasCustomStaticInvokeMethod()
MetaClassImplhasCustomStaticInvokeMethod in class MetaClassImplpublic CallSite createPogoCallSite(CallSite site, Object[] args)
MetaClassImplcreatePogoCallSite in class MetaClassImplpublic CallSite createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)
public MetaMethod retrieveConstructor(Object[] args)
MetaClassImplretrieveConstructor in class MetaClassImplpublic CallSite createConstructorSite(CallSite site, Object[] args)
MetaClassImplcreateConstructorSite in class MetaClassImpl