Overview | Package | Class | Deprecated | Index | Help
| Groovy Documentation
| FRAMES NO FRAMES
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD

## groovy.lang
[Java] Class ExpandoMetaClass

    java.lang.Object
      groovy.lang.MetaClassImpl
          groovy.lang.ExpandoMetaClass

All Implemented Interfaces:
  GroovyObject

    public class ExpandoMetaClass
    extends MetaClassImpl

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 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.

WARNING: This MetaClass uses a thread-bound ThreadLocal instance to store and retrieve properties. In addition properties stored use soft references so they are both bound by the life of the Thread and by the soft references. The implication here is you should NEVER use dynamic properties if you want their values to stick around for long periods because as soon as the JVM is running low on memory or the thread dies they will be garbage collected.

Authors:
  Graeme Rocher

Since:
  1.5

Nested Class Summary
`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.

Field Summary
`static java.lang.String` | `CONSTRUCTOR`
`static java.lang.String` | `STATIC_QUALIFIER`
`boolean` | `inRegistry`

Fields inherited from class MetaClassImpl
INVOKE_METHOD_METHOD, METHOD_MISSING, PROPERTY_MISSING, STATIC_METHOD_MISSING, STATIC_PROPERTY_MISSING, getPropertyMethod, invokeMethodMethod, isGroovyObject, isMap, metaMethodIndex, registry, setPropertyMethod, theCachedClass, theClass

Constructor Summary
`ExpandoMetaClass(java.lang.Class theClass)` Constructs a new ExpandoMetaClass instance for the given class
`ExpandoMetaClass(java.lang.Class theClass, MetaMethod[] add)`
`ExpandoMetaClass(java.lang.Class theClass, boolean register)` Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
`ExpandoMetaClass(java.lang.Class theClass, boolean register, MetaMethod[] add)`
`ExpandoMetaClass(java.lang.Class theClass, boolean register, boolean allowChangesAfterInit)` Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically

Method Summary
`void` | `addMixinClass(MixinInMetaClass mixin)`
`java.lang.Object` | `castToMixedType(java.lang.Object obj, java.lang.Class type)`
`protected void` | `checkInitalised()` Registers a new bean property
`CallSite` | `createConstructorSite(CallSite site, java.lang.Object[] args)`
`CallSite` | `createPogoCallCurrentSite(CallSite site, java.lang.Class sender, java.lang.String name, java.lang.Object[] args)`
`CallSite` | `createPogoCallSite(CallSite site, java.lang.Object[] args)`
`CallSite` | `createPojoCallSite(CallSite site, java.lang.Object receiver, java.lang.Object[] args)`
`CallSite` | `createStaticSite(CallSite site, java.lang.Object[] args)`
`ExpandoMetaClass` | `define(Closure closure)`
`static void` | `disableGlobally()` Call to disable the global use of ExpandoMetaClass
`static void` | `enableGlobally()` Call to enable global use of global use of ExpandoMetaClass within the registry.
`MetaMethod` | `findMixinMethod(java.lang.String methodName, java.lang.Class[] arguments)`
`java.util.List` | `getExpandoMethods()` Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
`java.util.Collection` | `getExpandoProperties()` Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
`java.util.Collection` | `getExpandoSubclassMethods()`
`java.lang.Class` | `getJavaClass()`
`MetaClass` | `getMetaClass()`
`MetaProperty` | `getMetaProperty(java.lang.String name)`
`java.util.List` | `getMethods()`
`java.util.List` | `getProperties()`
`java.lang.Object` | `getProperty(java.lang.String property)`
`java.lang.Object` | `getProperty(java.lang.Class sender, java.lang.Object object, java.lang.String name, boolean useSuper, boolean fromInsideClass)` Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
`java.lang.Object` | `getProperty(java.lang.Object object, java.lang.String name)` Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass
`java.lang.String` | `getPropertyForSetter(java.lang.String setterName)`
`protected java.lang.Object` | `getSubclassMetaMethods(java.lang.String methodName)` @return The Java class enhanced by this MetaClass
`boolean` | `hasMetaMethod(java.lang.String name, java.lang.Class[] args)` Returns true if the name of the method specified and the number of arguments make it a javabean property
`boolean` | `hasMetaProperty(java.lang.String name)` Determine if this method name suffix is a legitimate bean property name.
`void` | `initialize()`
`java.lang.Object` | `invokeConstructor(java.lang.Object[] arguments)`
`java.lang.Object` | `invokeMethod(java.lang.String name, java.lang.Object args)`
`java.lang.Object` | `invokeMethod(java.lang.Class sender, java.lang.Object object, java.lang.String methodName, java.lang.Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)`
`java.lang.Object` | `invokeStaticMethod(java.lang.Object object, java.lang.String methodName, java.lang.Object[] arguments)`
`protected boolean` | `isInitialized()` Checks if the meta class is initialized.
`boolean` | `isModified()`
`boolean` | `isSetter(java.lang.String name, CachedClass[] args)`
`static boolean` | `isValidExpandoProperty(java.lang.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(ExpandoMetaClass.Callable c)`
`void` | `refreshInheritedMethods(java.util.Set modifiedSuperExpandos)`
`void` | `registerBeanProperty(java.lang.String property, java.lang.Object newValue)`
`void` | `registerInstanceMethod(MetaMethod metaMethod)` Registers a new instance method for the given method name and closure on this MetaClass
`void` | `registerInstanceMethod(java.lang.String name, Closure closure)` Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
`protected void` | `registerStaticMethod(java.lang.String name, Closure callable)` Registers a new static method for the given method name and closure on this MetaClass
`protected void` | `registerStaticMethod(java.lang.String name, Closure callable, java.lang.Class[] paramTypes)`
`void` | `registerSubclassInstanceMethod(java.lang.String name, java.lang.Class klazz, Closure closure)`
`void` | `registerSubclassInstanceMethod(MetaMethod metaMethod)`
`protected void` | `setInitialized(boolean b)`
`void` | `setMetaClass(MetaClass metaClass)`
`void` | `setProperty(java.lang.String property, java.lang.Object newValue)`
`void` | `setProperty(java.lang.Class sender, java.lang.Object object, java.lang.String name, java.lang.Object newValue, boolean useSuper, boolean fromInsideClass)`

Methods inherited from class MetaClassImpl
addMetaBeanProperty, addMetaMethod, addMetaMethodToIndex, addNewInstanceMethod, addNewStaticMethod, applyPropertyDescriptors, checkIfGroovyObjectMethod, checkInitalised, chooseMethod, clearInvocationCaches, createConstructorSite, createPogoCallCurrentSite, createPogoCallSite, createPojoCallSite, createStaticSite, dropMethodCache, dropStaticMethodCache, findMethodInClassHierarchy, findMixinMethod, findOwnMethod, findPropertyInClassHierarchy, getAdditionalMetaMethods, getAttribute, getAttribute, getAttribute, getClassInfo, getClassNode, getEffectiveGetMetaProperty, getMetaMethod, getMetaMethods, getMetaProperty, getMethodWithCaching, getMethodWithoutCaching, getMethods, getProperties, getProperty, getProperty, getRegistry, getStaticMetaMethod, getSubclassMetaMethods, getSuperClasses, getTheCachedClass, getTheClass, getVersion, hasProperty, incVersion, initialize, invokeConstructor, invokeMethod, invokeMethod, invokeMethod, invokeMissingMethod, invokeMissingProperty, invokeStaticMethod, invokeStaticMissingProperty, isGroovyObject, isInitialized, isModified, onGetPropertyFoundInHierarchy, onInvokeMethodFoundInHierarchy, onMixinMethodFound, onSetPropertyFoundInHierarchy, onSuperMethodFoundInHierarchy, onSuperPropertyFoundInHierarchy, pickMethod, respondsTo, respondsTo, retrieveConstructor, retrieveStaticMethod, selectConstructorAndTransformArguments, setAttribute, setAttribute, setProperties, setProperty, setProperty, toString

Methods inherited from class java.lang.Object
java.lang.Object#wait(long), java.lang.Object#wait(long, int), java.lang.Object#wait(), java.lang.Object#equals(java.lang.Object), java.lang.Object#toString(), java.lang.Object#hashCode(), java.lang.Object#getClass(), java.lang.Object#notify(), java.lang.Object#notifyAll()

Field Detail

### CONSTRUCTOR

    public static final java.lang.String CONSTRUCTOR

### STATIC_QUALIFIER

    public static final java.lang.String STATIC_QUALIFIER

### inRegistry

    public boolean inRegistry

Constructor Detail

### ExpandoMetaClass

    public ExpandoMetaClass(java.lang.Class theClass)

  Constructs a new ExpandoMetaClass instance for the given class
  Parameters:
    `theClass` - The class that the MetaClass applies to

### ExpandoMetaClass

    public ExpandoMetaClass(java.lang.Class theClass, MetaMethod[] add)

### ExpandoMetaClass

    public ExpandoMetaClass(java.lang.Class theClass, boolean register)

  Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
  Parameters:
    `theClass` - The class that the MetaClass applies to
    `register` - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed

### ExpandoMetaClass

    public ExpandoMetaClass(java.lang.Class theClass, boolean register, MetaMethod[] add)

### ExpandoMetaClass

    public ExpandoMetaClass(java.lang.Class theClass, boolean register, boolean allowChangesAfterInit)

  Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically
  Parameters:
    `theClass` - The class that the MetaClass applies to
    `register` - True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
    `allowChangesAfterInit` - Should the meta class be modifiable after initialization. Default is false.

Method Detail

### addMixinClass

    public void addMixinClass(MixinInMetaClass mixin)

### castToMixedType

    public java.lang.Object castToMixedType(java.lang.Object obj, java.lang.Class type)

### checkInitalised

    protected void checkInitalised()

  Registers a new bean property
  Parameters:
    `property` - The property name
    `newValue` - The properties initial value

### createConstructorSite

    public CallSite createConstructorSite(CallSite site, java.lang.Object[] args)

### createPogoCallCurrentSite

    public CallSite createPogoCallCurrentSite(CallSite site, java.lang.Class sender, java.lang.String name, java.lang.Object[] args)

### createPogoCallSite

    public CallSite createPogoCallSite(CallSite site, java.lang.Object[] args)

### createPojoCallSite

    public CallSite createPojoCallSite(CallSite site, java.lang.Object receiver, java.lang.Object[] args)

### createStaticSite

    public CallSite createStaticSite(CallSite site, java.lang.Object[] args)

### define

    public ExpandoMetaClass define(Closure closure)

### disableGlobally

    public static void disableGlobally()

  Call to disable the global use of ExpandoMetaClass

### enableGlobally

    public static void enableGlobally()

  Call to enable global use of global use of ExpandoMetaClass within the registry. This has the advantage that inheritance will function correctly, but has a higher memory usage on the JVM than normal Groovy

### findMixinMethod

    public MetaMethod findMixinMethod(java.lang.String methodName, java.lang.Class[] arguments)

### getExpandoMethods

    public java.util.List getExpandoMethods()

  Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
  Returns:
    the expandoProperties

### getExpandoProperties

    public java.util.Collection getExpandoProperties()

  Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
  See Also:
    MetaClassImpl.invokeStaticMethod

### getExpandoSubclassMethods

    public java.util.Collection getExpandoSubclassMethods()

### getJavaClass

    public java.lang.Class getJavaClass()

### getMetaClass

    public MetaClass getMetaClass()

### getMetaProperty

    public MetaProperty getMetaProperty(java.lang.String name)

### getMethods

    public java.util.List getMethods()

### getProperties

    public java.util.List getProperties()

### getProperty

    public java.lang.Object getProperty(java.lang.String property)

### getProperty

    public java.lang.Object getProperty(java.lang.Class sender, java.lang.Object object, java.lang.String name, boolean useSuper, boolean fromInsideClass)

  Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass
  See Also:
    MetaClassImpl.getProperty

### getProperty

    public java.lang.Object getProperty(java.lang.Object object, java.lang.String name)

  Overrides default implementation just in case setProperty method has been overridden by ExpandoMetaClass
  See Also:
    MetaClassImpl.setProperty

### getPropertyForSetter

    public java.lang.String getPropertyForSetter(java.lang.String setterName)

### getSubclassMetaMethods

    protected java.lang.Object getSubclassMetaMethods(java.lang.String methodName)

  Returns:
    The Java class enhanced by this MetaClass

### hasMetaMethod

    public boolean hasMetaMethod(java.lang.String name, java.lang.Class[] args)

  Returns true if the name of the method specified and the number of arguments make it a javabean property
  Parameters:
    `name` - True if its a Javabean property
    `args` - The arguments
  Returns:
    True if it is a javabean property method

### hasMetaProperty

    public boolean hasMetaProperty(java.lang.String name)

  Determine if this method name suffix is a legitimate bean property name. Either the first or second letter must be upperCase for that to be true.

### initialize

    public void initialize()

### invokeConstructor

    public java.lang.Object invokeConstructor(java.lang.Object[] arguments)

### invokeMethod

    public java.lang.Object invokeMethod(java.lang.String name, java.lang.Object args)

### invokeMethod

    public java.lang.Object invokeMethod(java.lang.Class sender, java.lang.Object object, java.lang.String methodName, java.lang.Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass)

### invokeStaticMethod

    public java.lang.Object invokeStaticMethod(java.lang.Object object, java.lang.String methodName, java.lang.Object[] arguments)

### isInitialized

    protected boolean isInitialized()

  Checks if the meta class is initialized.
  See Also:
    MetaClassImpl.isInitialized

### isModified

    public boolean isModified()

### isSetter

    public boolean isSetter(java.lang.String name, CachedClass[] args)

### isValidExpandoProperty

    public static boolean isValidExpandoProperty(java.lang.String property)

### onGetPropertyFoundInHierarchy

    protected void onGetPropertyFoundInHierarchy(MetaMethod method)

### onInvokeMethodFoundInHierarchy

    protected void onInvokeMethodFoundInHierarchy(MetaMethod method)

### onSetPropertyFoundInHierarchy

    protected void onSetPropertyFoundInHierarchy(MetaMethod method)

### onSuperMethodFoundInHierarchy

    protected void onSuperMethodFoundInHierarchy(MetaMethod method)

### onSuperPropertyFoundInHierarchy

    protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property)

### performOperationOnMetaClass

    protected void performOperationOnMetaClass(ExpandoMetaClass.Callable c)

### refreshInheritedMethods

    public void refreshInheritedMethods(java.util.Set modifiedSuperExpandos)

### registerBeanProperty

    public void registerBeanProperty(java.lang.String property, java.lang.Object newValue)

### registerInstanceMethod

    public void registerInstanceMethod(MetaMethod metaMethod)

  Registers a new instance method for the given method name and closure on this MetaClass
  Parameters:
    metaMethod

### registerInstanceMethod

    public void registerInstanceMethod(java.lang.String name, Closure closure)

  Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods
  Returns:
    A list of MetaMethods
  See Also:
    MetaObjectProtocol.getMethods

### registerStaticMethod

    protected void registerStaticMethod(java.lang.String name, Closure callable)

  Registers a new static method for the given method name and closure on this MetaClass
  Parameters:
    `name` - The method name
    `callable` - The callable Closure

### registerStaticMethod

    protected void registerStaticMethod(java.lang.String name, Closure callable, java.lang.Class[] paramTypes)

### registerSubclassInstanceMethod

    public void registerSubclassInstanceMethod(java.lang.String name, java.lang.Class klazz, Closure closure)

### registerSubclassInstanceMethod

    public void registerSubclassInstanceMethod(MetaMethod metaMethod)

### setInitialized

    protected void setInitialized(boolean b)

### setMetaClass

    public void setMetaClass(MetaClass metaClass)

### setProperty

    public void setProperty(java.lang.String property, java.lang.Object newValue)

### setProperty

    public void setProperty(java.lang.Class sender, java.lang.Object object, java.lang.String name, java.lang.Object newValue, boolean useSuper, boolean fromInsideClass)

Groovy Documentation