More about Methods

A method name can be any character string. When an object receives a message, Rexx searches for a method whose name matches the message name.

You must surround a method name with quotation marks when it is the same as an operator. The following example illustrates how to do this correctly. It creates a new class (Cost), defines a new method (%), creates an instance of the Cost class (Mycost), and sends a % message to Mycost:


mycost = Cost~new           /* Creates new instance mycost.*/
mycost~"%"                  /* Sends % message to mycost.  */

::class Cost subclass "Retail" /* Creates Cost, a sub-     */
                               /* class of "Retail" class. */
  ::method "%"                 /* Creates % method.        */
    expose p                   /* Produces: Enter a price. */
    say "Enter a price"        /* If the user specifies a  */
    pull p                     /* price of 100,            */
    say p*1.07                 /* produces: 107            */
    return 0

The Default Search Order for Selecting a Method

When a message is sent to an object, Rexx looks for a method whose name matches the message string. If the message is ADD, for example, Rexx looks for a method named ADD. Because, in the class hierarchy, there may be more than one method with the same name, Rexx begins its search at the object specified in the message. If the sought method is not found there, the search continues up the hierarchy. Rexx searches in the following order:

  1. A method the object defines itself (with SETMETHOD or ENHANCED).

  2. A method the object's class defines.

    An object acquires the methods of its parent class; that is, the class for which the object was created. If the class subsequently receives new methods, objects predating the new methods do not acquire them.

  3. A method an object's superclasses define.

    As with the object's class, only methods that existed in the superclass when the object was created are valid. Rexx searches the superclass method definitions in the order that INHERIT messages were sent to an object's class.

If Rexx does not find a match for the message name, Rexx checks the object for method name UNKNOWN. If it exists, Rexx calls the UNKNOWN method, and returns whatever the UNKNOWN method returns. For more information on the UNKNOWN method, see Defining an UNKNOWN Method. If the object does not have an UNKNOWN method, Rexx raises a NOMETHOD condition. Any trapped information can then be inspected using Rexx's CONDITION built-in function.

Rexx searches up the hierarchy so that methods existing in higher levels can be supplemented or overridden by methods existing in lower levels.

Figure 6-4. Searching the Hierarchy for a Method

For example, suppose you wrote a program that allows users to look up other users' phone numbers. Your program includes a class called Phone_Directory, and all its instances are users' names with phone numbers. You have included a method in Phone_Directory called NOTIFY that reports some data to a file whenever someone looks up a number. All instances of Phone_Directory use the NOTIFY method.

Now you decide you want NOTIFY, in addition to its normal handling, to personally inform you whenever anyone looks up your number. To accommodate this special case for your name only, you create your own NOTIFY method that adds the new task and replicates the file-handling task. You save the new method as part of your own name instance, retaining the same name, NOTIFY.

Now, when a NOTIFY message is sent to your name instance, the new version of NOTIFY is found first. Rexx does not look further up the class hierarchy. The instance-level version overrides the version at the class level. This technique of overriding lets you change a method used by one instance without disturbing the common method used by all the other instances. It is very powerful for that reason.

Changing the Search Order for Methods

When composing a message, you can change the default search order for methods by doing both of the following:

  1. Making the receiver object the sender object. You usually do this by specifying the special variable SELF. SELF holds the value of the object in which a method is running.

  2. Specifying a colon and a starting scope after the message name. The starting scope is a variable or environment symbol that identifies the scope object to use as the method search starting point. This scope object can be:

    • A direct superclass of the class that defines the active method

    • The object itself (for example, the value of the variable SELF), if you used SETMETHOD to add methods to the object.

      The scope variable is usually the special variable SUPER, but it can be any environment symbol or variable name whose value is a valid class.

In A Sample Program Using Directives, an Account subclass of the Object superclass is created. It defines a TYPE method for Account, and creates the Savings subclass of Account. The example defines a TYPE method for the Savings subclass, as follows:

::class Savings subclass Account

  ::method "TYPE"
    return "a savings account"

To change the search order so Rexx searches for TYPE in the Account rather than Savings subclass, enter this instead:

  ::method "TYPE"
    return self~type:super "(savings)"

When you create an asav instance of the Savings subclass and send a TYPE message to asav:

say asav~type

Rexx displays:

an account

rather than:

a savings account

because Rexx searches for TYPE in the Account class first.

Public versus Private Methods

A method can be public or private. Any object can send a message that runs a public method. A private method can only be invoked from specific calling contexts. These contexts are:

  1. From within a method owned by the same class as the target. This is frequently the same object, accessed via the special variable SELF. Private methods of an object can also be accessed from other instances of the same class (or subclass instances).

  2. From within a method defined at the same class scope as the method. For example:

    ::class Savings
    ::method newCheckingAccount CLASS
      instance = self~new
      instance~makeChecking
      return instance
    
    ::method makeChecking private
      expose checking
      checking = .true

    The newCheckingAccount CLASS method is able to invoke the makeChecking method because the scope of the makeChecking method is .Savings.

  3. From within an instance (or subclass instance) of a class to a private class method of its class. For example:

    ::class Savings
    ::method init class
      expose counter
      counter = 0
    
    ::method allocateAccountNumber private class
      expose counter
      counter = counter + 1
      return counter
    
    ::method init
      expose accountNumber
      accountNumber = self~class~allocateAccountNumber

    The instance INIT method of the Savings class is able to invoke the allocateAccountNumber private method of the .Savings class object because it is owned by an instance of the .Savings class.

Private methods include methods at different scopes within the same object. This allows superclasses to make methods available to their subclasses while hiding those methods from other objects. A private method is like an internal subroutine. It shields the internal information of an object to outsiders, but allowing objects to share information with each other and their defining classes.

Defining an UNKNOWN Method

When an object that receives a message has no matching message name, Rexx checks if the object has a method named UNKNOWN. If it does, Rexx calls UNKNOWN, passing two arguments. The first is the name of the method that was not located. The second is an array containing the arguments passed with the original message.