Types of Classes

There are four kinds of classes:

The following sections explain these.

Object Classes

An object class is 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 the new or changed methods.

The instance variables within an object are created on demand whenever a method EXPOSEs an object variable. The class creates the object instance, defines the methods the object has, and the object instance completes the job of constructing the object.

The String class and the Array Class are examples of object classes.

Mixin Classes

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. 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. For example, a mixin class created as a subclass of the Array class can only be inherited by other Array subclasses. Mixins that use the Object class as a base class can be inherited by any class.

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

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. It is possible to create abstract methods on a class. An abstract method is a placeholder that subclasses are expected to override. Failing to provide a real method implementation will result in an error when the abstract version is called.

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 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 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       */