This chapter provides an overview of the Rexx class structure.
A Rexx object consists of object methods and object variables ("attributes"). Sending a message to an object causes the object to perform some action; a method whose name matches the message name defines the action that is performed. Only an object's methods can access the object variables belonging to an object. EXPOSE instructions within an object's methods specify object variables. Any variables not exposed are dropped on return from a method.
You can create an object by sending a message to a class object. An object created from a class is an instance of that class. Classes define the methods and method names for their instances. The methods a class defines for its instances are called the instance methods of that class. These are the object methods for the instances. Classes can also define class methods, which are a class's own object methods.
Note: When referring to object methods (for objects other than classes) or instance methods (for classes), this book uses the term methods when the meaning is clear from the context. When referring to object methods and class methods of classes, this book uses the qualified terms to avoid possible confusion.
There are four kinds of classes:
Object classes
Mixin classes
Abstract classes
Metaclasses
The following sections explain these.
An object class is like a factory for producing objects. An object class creates objects (instances) and provides methods that these objects can use. An object acquires the instance methods of the class to which it belongs at the time of its creation. If a class gains additional methods, objects created before the definition of these methods do not acquire these methods.
Because the object methods also define the object variables, object classes are factories for creating Rexx objects. The Array class (see The Array Class) is an example of an object class.
Classes can inherit from more than the single superclass from which they were created. This is called multiple inheritance. Classes designed to add a set of instance and class methods to other classes are called mixin classes, or simply mixins.
You can add mixin methods to an existing class by sending an INHERIT message or using the INHERIT option on the ::CLASS directive. (See Directives.) In either case, the class to be inherited must be a mixin. During both class creation and multiple inheritance, subclasses inherit both class and instance methods from their superclasses.
Mixins are always associated with a base class, which is the mixin's first non-mixin superclass. Any subclass of the mixin's base class can (directly or indirectly) inherit a mixin; other classes cannot.
To create a new mixin class, you send a MIXINCLASS message to an existing class or use the ::CLASS directive with the MIXINCLASS option. A mixin class is also an object class and can create instances of the class.
Abstract classes provide definitions for instance methods and class methods but are not intended to create instances. Abstract classes often define the message interfaces that subclasses should implement.
You create an abstract class like object or mixin classes. No extra messages or keywords on the ::CLASS directive are necessary. Rexx does not prevent users from creating instances of abstract classes.
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 (see 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 for statistics */ ::class statArray subclass array public ::method init /* Initialize running total and forward to superclass */ expose total total = 0 /* init describes the init method. */ 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 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 */