ExpandoMetaClass (Groovy 5.0.6)

Package: groovy.lang

## [Java] Class ExpandoMetaClass

- groovy.lang.ExpandoMetaClass

- All Implemented Interfaces and Traits:
    GroovyObject

      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.

  Since:
    1.5

- - ### Nested Class Summary

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

  - ### Field Summary

    Fields
    Modifiers | Name | Description
    --- | --- | ---
    `static String` | `CONSTRUCTOR` |
    `static String` | `STATIC_QUALIFIER` |
    `boolean` | `inRegistry` |

  - Inherited fields
    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 Summary

    Constructors
    Constructor and description
    `ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)`
    `ExpandoMetaClass(MetaClassRegistry registry, Class theClass, boolean register, boolean allowChangesAfterInit, MetaMethod[] add)`
    `ExpandoMetaClass(Class theClass)` Constructs a new ExpandoMetaClass instance for the given class
    `ExpandoMetaClass(Class theClass, MetaMethod[] add)`
    `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)`
    `ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit)` Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass in the MetaClassRegistry automatically

  - ### Methods Summary

    Methods
    Type Params | Return Type | Name and description
    --- | --- | ---
    `` | `public void` | `addMixinClass(MixinInMetaClass mixin)`
    `` | `public void` | `call()`
    `` | `public Object` | `castToMixedType(Object obj, Class type)`
    `` | `protected void` | `checkInitalised()`
    `` | `public CallSite` | `createConstructorSite(CallSite site, Object[] args)`
    `` | `public CallSite` | `createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)`
    `` | `public CallSite` | `createPogoCallSite(CallSite site, Object[] args)`
    `` | `public CallSite` | `createPojoCallSite(CallSite site, Object receiver, Object[] args)`
    `` | `public CallSite` | `createStaticSite(CallSite site, Object[] args)`
    `` | `public ExpandoMetaClass` | `define(Closure closure)`
    `` | `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)`
    `` | `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()`
    `` | `public Class` | `getJavaClass()` Returns: The Java class enhanced by this MetaClass
    `` | `public MetaClass` | `getMetaClass()`
    `` | `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()`
    `` | `public Object` | `getProperty(String property)`
    `` | `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)`
    `` | `public boolean` | `hasCustomStaticInvokeMethod()`
    `` | `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()` {@inheritDoc}
    `` | `public Object` | `invokeConstructor(Object[] arguments)`
    `` | `public Object` | `invokeMethod(String name, Object args)`
    `` | `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()`
    `` | `public boolean` | `isSetter(String name, CachedClass[] args)`
    `` | `public 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(ExpandoMetaClass.Callable c)`
    `` | `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(MetaMethod metaMethod)` Registers a new instance method for the given method name and closure on this MetaClass
    `` | `public 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
    `` | `public void` | `registerSubclassInstanceMethod(String name, Class klazz, Closure closure)`
    `` | `public void` | `registerSubclassInstanceMethod(MetaMethod metaMethod)`
    `` | `public MetaMethod` | `retrieveConstructor(Object[] args)`
    `` | `protected void` | `setInitialized(boolean b)`
    `` | `public void` | `setMetaClass(MetaClass metaClass)`
    `` | `public void` | `setProperty(String property, Object newValue)`
    `` | `public void` | `setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass)`

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class MetaClassImpl` | `getMetaMethod, getMetaProperty, getRegistry, getStaticMetaMethod, getSuperClasses, getTheCachedClass, getTheClass, hasProperty, isGroovyObject, methodNameAction, respondsTo, respondsTo, skipClass`

- - ### Field Detail

    - #### public static final String CONSTRUCTOR

    - #### public static final String STATIC_QUALIFIER

    - #### public boolean inRegistry

  - ### Constructor Detail

    - #### 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)

      Constructs a new ExpandoMetaClass instance for the given class

      Parameters:
        `theClass` - The class that the MetaClass applies to

    - #### public ExpandoMetaClass(Class theClass, MetaMethod[] add)

    - #### public ExpandoMetaClass(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 affect all instances if changed

    - #### public ExpandoMetaClass(Class theClass, boolean register, MetaMethod[] add)

    - #### public ExpandoMetaClass(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 affect all instances if changed
        `allowChangesAfterInit` - Should the metaclass be modifiable after initialization. Default is false.

  - ### Method Detail

    - #### public void addMixinClass(MixinInMetaClass mixin)

    - #### @Override
      public void call()

    - #### public Object castToMixedType(Object obj, Class type)

    - #### @Override
      protected void checkInitalised()

    - #### @Override
      public CallSite createConstructorSite(CallSite site, Object[] args)

    - #### public CallSite createPogoCallCurrentSite(CallSite site, Class sender, String name, Object[] args)

    - #### @Override
      public CallSite createPogoCallSite(CallSite site, Object[] args)

    - #### @Override
      public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args)

    - #### @Override
      public CallSite createStaticSite(CallSite site, Object[] args)

    - #### public ExpandoMetaClass define(@ClosureParams(value=SimpleType.class, options="java.lang.Object")
      @DelegatesTo(value=DefiningClosure.class, strategy=Closure.DELEGATE_ONLY)
      Closure closure)

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

    - #### @Override
      public MetaMethod findMixinMethod(String methodName, Class[] arguments)

    - #### public List<MetaMethod> getExpandoMethods()

      Returns a list of expando MetaMethod instances added to this ExpandoMetaClass

      Returns:
        the expandoMethods

    - #### public Collection<MetaProperty> getExpandoProperties()

      Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass

      Returns:
        the expandoProperties

    - #### public Collection getExpandoSubclassMethods()

    - #### public Class getJavaClass()

      Returns:
        The Java class enhanced by this MetaClass

    - #### @Override
      public MetaClass getMetaClass()

    - #### @Override
      public MetaProperty getMetaProperty(String name)

      Looks up an existing MetaProperty by name

      Parameters:
        `name` - The name of the MetaProperty
      Returns:
        The MetaProperty or null if it doesn't exist

    - #### @Override
      public List<MetaMethod> getMethods()

      Overrides the behavior of parent getMethods() method to make MetaClass aware of added Expando methods

      Returns:
        A list of MetaMethods
      See Also:
        MetaObjectProtocol.getMethods

    - #### @Override
      public List<MetaProperty> getProperties()

    - #### @Override
      public Object getProperty(String property)

    - #### @Override
      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

      See Also:
        MetaClassImpl.getProperty

    - #### @Override
      public Object getProperty(Object object, String name)

      Overrides default implementation just in case getProperty method has been overridden by ExpandoMetaClass

      See Also:
        MetaClassImpl.getProperty

    - #### public String getPropertyForSetter(String setterName)

      Returns a property name equivalent for the given setter name or null if it is not a getter

      Parameters:
        `setterName` - The setter name
      Returns:
        The property name equivalent

    - #### @Override
      protected Object getSubclassMetaMethods(String methodName)

    - #### @Override
      public boolean hasCustomStaticInvokeMethod()

    - #### public boolean hasMetaMethod(String name, Class[] args)

      Checks whether a MetaMethod for the given name and arguments exists

      Parameters:
        `name` - The name of the MetaMethod
        `args` - The arguments to the meta method
      Returns:
        True if the method exists otherwise null

    - #### public boolean hasMetaProperty(String name)

      Returns true if the MetaClass has the given property

      Parameters:
        `name` - The name of the MetaProperty
      Returns:
        True it exists as a MetaProperty

    - #### @Override
      public void initialize()

      {@inheritDoc}

    - #### @Override
      public Object invokeConstructor(Object[] arguments)

    - #### @Override
      public Object invokeMethod(String name, Object args)

    - #### @Override
      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

      See Also:
        MetaClassImpl.invokeMethod

    - #### @Override
      public Object invokeStaticMethod(Object object, String methodName, Object[] arguments)

      Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass

      See Also:
        MetaClassImpl.invokeStaticMethod

    - #### @Override
      protected boolean isInitialized()

      Checks if the metaclass is initialized.

      See Also:
        MetaClassImpl.isInitialized

    - #### @Override
      public boolean isModified()

    - #### public boolean isSetter(String name, CachedClass[] args)

    - #### public static boolean isValidExpandoProperty(String property)

    - #### @Override
      protected void onGetPropertyFoundInHierarchy(MetaMethod method)

    - #### @Override
      protected void onInvokeMethodFoundInHierarchy(MetaMethod method)

    - #### @Override
      protected void onSetPropertyFoundInHierarchy(MetaMethod method)

    - #### @Override
      protected void onSuperMethodFoundInHierarchy(MetaMethod method)

    - #### @Override
      protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property)

    - #### protected void performOperationOnMetaClass(ExpandoMetaClass.Callable c)

    - #### public void refreshInheritedMethods(Set modifiedSuperExpandos)

      Called from ExpandoMetaClassCreationHandle in the registry if it exists to set up inheritance handling

      Parameters:
        `modifiedSuperExpandos` - A list of modified super ExpandoMetaClass

    - #### public void registerBeanProperty(String property, Object newValue)

      Registers a new bean property

      Parameters:
        `property` - The property name
        `newValue` - The properties initial value

    - #### public void registerInstanceMethod(MetaMethod metaMethod)

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

      Parameters:
        metaMethod

    - #### public 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

      Parameters:
        `name` - The method name
        `callable` - The callable Closure

    - #### public void registerSubclassInstanceMethod(String name, Class klazz, Closure closure)

    - #### public void registerSubclassInstanceMethod(MetaMethod metaMethod)

    - #### @Override
      public MetaMethod retrieveConstructor(Object[] args)

    - #### @Override
      protected void setInitialized(boolean b)

    - #### @Override
      public void setMetaClass(MetaClass metaClass)

    - #### @Override
      public void setProperty(String property, Object newValue)

    - #### @Override
      public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass)