A metaclass is a class you can use to create another class. Rexx provides just one metaclass, the Class class. A class is a factory for creating instances, and the Class class is a factory for creating factories. Whenever you create a new factory, or class, the new class is an instance of Class. The instance methods of Class provide the operations needed to run the new factories. These instance methods are inherited by the new factory as its class methods.
The classes Rexx provides do not permit changes or additions to their method definitions. As a result, all new factories inherit these unchangeable actions from the Class class, and thus operate the same way. So if you want to create a new class--a new factory--that behaves differently from the others, you can do either of the following:
Write additional class methods for the new class, using the ::METHOD directive with the CLASS option
Use a metaclass
If you plan to create many factories with the same operational changes, you use the metaclass.
Any metaclass you create is a subclass of the Class class. To make your own metaclass, specify class as a SUBCLASS option in the ::CLASS directive:
/* Create a new metaclass */ ::class your_metaclass subclass class
The instance methods of your_metaclass becomes the class methods for any new class created using your_metaclass. For example, you could create a metaclass called InstanceCounter that includes instance methods for tracking how many instances the class creates:
/* Create a new metaclass that counts its instances */ ::class InstanceCounter subclass class ::method init ...
Instead of having to add instance-counting class methods to other new classes you write, you can make InstanceCounter their metaclass. When you create the new class, you specify InstanceCounter as a METACLASS option in the ::CLASS directive. Creating a Point class might look like this:
/* Create a public Point class using the InstanceCounter metaclass */ ::class point public metaclass InstanceCounter ::method init ...
The instance methods in your new InstanceCounter metaclass become the class methods of the Point class, and any other classes that you create in the future using a similar directive. Here is a complete example:
/* A metaclass example */ a = .point~new(1,1) /* Create point instances */ say "Created point instance" a /* a, b, and c. */ 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." /* Create a new metaclass that */ /* counts its instances. */ ::class InstanceCounter subclass class ::method init /* Create an INIT method to */ expose instanceCount /* initialize instanceCount. */ instanceCount = 0 /* Forward INIT to superclass. */ .message~new(self, .array~of("INIT",super), "a", arg(1,"A"))~send ::method new /* Create a NEW instance method.*/ expose instanceCount /* Create a new instance. */ instanceCount = instanceCount + 1 /* Bump the count. */ /* Forward NEW to superclass. */ return .message~new(self, .array~of("NEW",super), "a", arg(1,"A"))~send ::method instances /* Create an INSTANCES method. */ expose instanceCount /* Return the instance count. */ return instanceCount /* Create Point class using */ /* InstanceCounter metaclass. */ ::class point public metaclass InstanceCounter ::method init /* Create an INIT method. */ expose xVal yVal /* Set object variables */ use arg xVal, yVal /* as passed on NEW. */ ::method string /* Create a STRING method. */ expose xVal yVal /* Use object variables */ return "("xVal","yVal")" /* to return string value. */