Chapter 9. Tutorial III

Table of Contents

9.1. Classes and OOP

9.1. Classes and OOP

Gamma implements object-oriented programming (OOP) features which provide a single-inheritance class mechanism with instance variables and methods. Since Gamma is an interpreter, the object definitions are truly dynamic, allowing for run-time extensibility. This example provides the simplest of starting points to this key software methodology.

#!/usr/cogent/bin/gamma

/*
 *  Demonstrates:
 *  class definitions: attributes and methods.
 *  constructors and destructors
 *  method overloading
 */

/*
 * Define a class of animal, with no default type and a default of 4 legs
 */
class animal
{
    type = "animal";
    num_legs = 4;       // By default, animals have 4 legs
}

/*
 * The constructor for an animal is called when any instance of animal
 * or a subclass of animal is created using a call to 'new'.  Constructors
 * have no arguments.
 */
method animal.constructor()
{
    princ ("A ", class_name(class_of(self)), " is born\n");
}

/*
 * The destructor for an animal is called when any instance of animal
 * or a subclass of animal is deleted by the garbage collector.  There
 * is no explicit deletion mechanism in Gamma.  Destructors have no
 * arguments.
 */
method animal.destructor ()
{
    princ ("A ", class_name(class_of(self)), " dies\n");
}

/*
 * All methods except constructor and destructor are overloaded, meaning
 * that only the method for the nearest class in the ancestry of the
 * instance will be called for any given method name.
 */
method animal.describe ()
{
    princ ("The ", self.type, " has ", self.num_legs, " legs.\n");
}

/*
 * Create a subclass of animal of a particular type.
 */

class cat animal
{
    type = "feline";
}

/*
 * Create another subclass of animal which is itself a parent class
 */

class insect animal
{
    num_wings = 2;
    num_legs = 6;
}

/*
 * Overload the description method for insects so we hear about
 * wings and legs when we ask about insects.
 */

method insect.describe ()
{
    /*
     * We can explicitly call a method of a parent class using the
     * 'call' function and naming a parent class.
     */
    call (self, #animal, #describe);
    princ ("	(oh, and ", self.num_wings, " wings)\n");
}

/*
 * Create a destructor for an insect.  This will be run before the
 * animal destructor.
 */

method insect.destructor ()
{
    princ ("Crunch. ");
}

/*
 * Create a subclass of an insect which is a particular type.
 */

class beetle insect
{
    type = "rhinoceros beetle";
    num_wings = 4;
}

function main ()
{
    local		pet = new (cat);
    local		bug = new (beetle);

    /*
     * cat gets its describe method from the animal class
     */
    pet.describe();

    /*
     * beetle gets its describe method from the insect class
     */
    bug.describe();

    /*
     * Since the destructor will be implicitly called by the garbage
     * collector, we can cause the destructor to occur by removing
     * all references to the instances (set the variables referencing
     * the instances to nil), and then explicitly invoke the garbage
     * collector.  Typically this is not necessary, as the garbage
     * collector will run when necessary.
     */
    pet = nil;
    bug = nil;

    gc();
}