Metaclasses

A metaclass is a class you can use to create another class. The only metaclass that Rexx provides is .Class, the Class class. The Class class is the metaclass of all the classes Rexx provides. This means that instances of .Class are themselves classes. The Class class is like a factory for producing the factories that produce objects.

To change the behavior of an object that is an instance, you generally use subclassing. For example, you can create statArray, a subclass of the Array class. The statArray class can include a method for computing a total of all the numeric elements of an array.


Creating an Array subclass
/* Creating an array subclass for statistics */
::class statArray subclass array public
::method init /* Initialize running total and forward to superclass */
expose total
total = 0
forward class (super)
::method put /* Modify to increment running total */
expose total
use arg value
total = total + value /* Should verify that value is numeric!!! */
forward class (super)
::method "[]=" /* Modify to increment running total */
forward message "PUT"
::method remove /* Modify to decrement running total */
expose total
use arg index
forward message "AT" continue
total = total - result
forward class (super)
::method average /* Return the average of the array elements */
expose total
return total / self~items
::method total /* Return the running total of the array elements */
expose total
return total

You can use this method on the individual array instances, so it is an instance method.

However, if you want to change the behavior of the factory producing the arrays, you need a new class method. One way to do this is to use the ::METHOD directive with the CLASS option. Another way to add a class method is to create a new metaclass that changes the behavior of the statArray class. A new metaclass is a subclass of .class.

You can use a metaclass by specifying it in a SUBCLASS or MIXINCLASS message or on a ::CLASS directive with the METACLASS option.

If you are adding a highly specialized class method useful only for a particular class, use the ::METHOD directive with the CLASS option. However, if you are adding a class method that would be useful for many classes, such as an instance counter that counts how many instances a class creates, you use a metaclass.

The following examples add a class method that keeps a running total of instances created. The first version uses the ::METHOD directive with the CLASS option. The second version uses a metaclass.

Version 1


Adding a class method
/* Adding a class method using ::METHOD */
a = .point~new(1,1) /* Create some point instances */
say "Created point instance" a
b = .point~new(2,2) /* create another point instance */
say "Created point instance" b
c = .point~new(3,3) /* create another point instance */
say "Created point instance" c
/* ask the point class how many */
/* instances it has created */
say "The point class has created" .point~instances "instances."
::class point public /* create Point class */
::method init class
expose instanceCount
instanceCount = 0 /* Initialize instanceCount */
forward class (super) /* Forward INIT to superclass */
::method new class
expose instanceCount /* Creating a new instance */
instanceCount = instanceCount + 1 /* Bump the count */
forward class (super) /* Forward NEW to superclass */
::method instances class
expose instanceCount /* Return the instance count */
return instanceCount
::method init
expose xVal yVal /* Set object variables */
use arg xVal, yVal /* as passed on NEW */
::method string
expose xVal yVal /* Use object variables */
return "("xVal","yVal")" /* to return string value */

Version 2

/* Adding a class method using a metaclass */
a = .point~new(1,1) /* Create some point instances */
say "Created point instance" a
b = .point~new(2,2)
say "Created point instance" b
c = .point~new(3,3)
say "Created point instance" c
/* ask the point class how many */
/* instances it has created */
say "The point class has created" .point~instances "instances."
::class InstanceCounter subclass class /* Create a new metaclass that */
/* will count its instances */
::method init
expose instanceCount
instanceCount = 0 /* Initialize instanceCount */
forward class (super) /* Forward INIT to superclass */
::method new
expose instanceCount /* Creating a new instance */
instanceCount = instanceCount + 1 /* Bump the count */
forward class (super) /* Forward NEW to superclass */
::method instances
expose instanceCount /* Return the instance count */
return instanceCount
::class point public metaclass InstanceCounter /* Create Point class */
/* using InstanceCounter metaclass */
::method init
expose xVal yVal /* Set object variables */
use arg xVal, yVal /* as passed on NEW */
::method string
expose xVal yVal /* Use object variables */
return "("xVal","yVal")" /* to return string value */