next up previous contents index
Next: Inheritance Up: Implementations Previous: Parameterized Implementations


Classes implement user-defined types and user-defined parameterized types. A class has the following form:

        <class_def> -> <idn> "=" class [<parms>] [<for_type>] [<inherits>]
                [<where>] [<provides>]   [<hides>]
                end <idn>
        <equate_or_ivar_decl> -> <equate> | <ivar_decl>
        <equate_or_routine_def> -> <equate> | <routine_def>
The initial idn names the class, and the final idn must match it. The inherits, provides, and hides clauses are part of the inheritance mechanism (10.5).

The for_type clause names the type implemented by the class:

        <for_type> -> for <type_designator>
If this clause is missing, the class does not implement a type. The primary use of such a class is as a superclass to classes defined in other modules (10.5.1); the class could also be used privately, within its own module.

A class is a scope in which the idns that name the formal parameters are defined. Therefore these names can be used within the class to refer to the respective parameters. The where clause of the class lists the constraints on the parameters.

The body of a class has two main parts: a set of declarations and a set of method implementations. All the names in these two sets must be distinct.

The declarations define the instance variables, which are used to represent the state of objects of the class. Every object of the class has its own instance variables; the values of its instance variables define the state of the object. An instance variable is declared using a special form:

<ivar_decl> -> <decl> | <decl_with_impls>
The decl_with_impls form is used to provide abbreviated implementations (10.4.1) of some methods.

Following the instance variable declarations are implementations of the methods. A class must implement all methods of its type (although some of these implementations can be inherited (10.5) or abbreviated (10.4.1)); these are its ``public'' methods. A class that does not implement a type has no public methods. The signatures of routine_defs that implement public methods must have types that are subtypes of those given in the type specification. In addition a class can implement some ``private'' methods that can be used only within the class and its module and possibly within subclasses of the class (10.5).

A class name (or the name plus the actual parameters if the class is parameterized (10.3)) can be used as a type within the class or its module. We refer to this type as the _class type_. The class type describes the set of objects implemented by the class. Objects of this type have a field corresponding to each of the instance variables and all the public and private methods defined by the class. The ``dot'' notation is used to access the object's instance variables and select it methods, e.g.,

        % assume class C has instance variable v and method m
        x: C		% x is an object of class type C
        x.m(...)	% selects m from x and calls it
        x.v := ...	% assigns to x's instance variable v
        y := ...x.v...	% gets value of x's instance variable v

In addition to its regular arguments, a method has an implicit argument that it can refer to by using the keyword self. This argument refers to the method's object. The method can use self to access the instance variables and select the methods belonging to its object, e.g., "`self`.v", or it can access instance variables and select methods of its object without using the ``dot'' notation, just by using their names, e.g., "v" means the same thing as "`self`.v".

If the class implements a type, its class type is a subtype of this type. For example, within a class "C" or its module, the statement

typecase x	 	% assume x: T
    when C (y):		% in here y refers to x as a C
allows the use the form "y.v" to access "y"'s instance variable "v" within the when arm. The arm will be selected if the actual object is a "C" or some subclass of "C" (10.5).

Within a class and its module, new objects of the class type can be obtained by calling the class constructor (7.4).

Abbreviated Implementations

Sometimes methods merely provide access to instance variables, either to get the value of the instance variable, or (more rarely) to set the value. Methods like these can be implemented using a short form by annotating the instance variable:
<decl_with_impls> -> <idn1> ":" <type_designator> implements <idn2> ["," <idn3>]
Here, idn1 is the name of a variable being declared, idn2 is the name of a method to get the value of the variable, and idn3 (if present) is the name of a method to set the value of the variable. A public get method must be a procedure that takes no arguments and returns a "T" where "T" is a supertype of the type of the associated instance variable; a public set method must be a procedure that has no return values and takes one argument of type "S", where S is a subtype of the type of the associated instance variable. No restrictions are placed on the exceptions listed in the type of the get or the set method. For example, it is okay to provide abbreviated implementations for the following methods:
size () returns (int) signals (unknown)
set_name(name: string) signals (permission_denied)
An abbreviated implementation for a method is permitted only if the class provides no other implementation for the method. Here is an example of the use of abbreviated implementations:
point = type
    x( ) returns (int)   % returns value of x coordinate
    set_x(v: int)        % changes value of x coordinate to v
    y( ) returns (int)   % returns value of y coordinate
    set_y(v: int)        % changes value of y coordinate to v
end point

c = class for point
    x_coord: int implements x, set_x
    y_coord: int implements y, set_y
end c



Within a class a special procedure is available for determining whether two objects of that class are actually the very same object. This procedure has the routine_interface:

same_object (x: C, y: any) returns(bool)
where "C" is the name of the class. The procedure returns true if "x" and "y" denote the same object and false otherwise. For example, the "equal" method for a mutable type might call "`same_object`(`self`, z)" to determine whether object "z" is the same object as self.

The same_object procedure is available only within the class. It cannot be used by other code in the class's module, and it cannot be exported by the class.


Here is an implementation of the "bag" type whose specification was given in Section 9.4.

module implements create_bag

brep = class[T] for bag[T]

        sz: int implements size 	% implementation of size method
        els: array[T]

        % the rep invariant is:  sz = els.size( )

        put (x: T) 
                sz := sz + 1
                end put

        get ( ) returns (T) signals (empty)
                x: T := els.remove( ) except when limits: signal empty end
                sz := sz - 1
                return (x)
                end get

        copy ( ) returns (bag[T])
                where T has copy ( ) returns (T)
                return (brep[T]{sz := sz, els := els.copy( )})
                end copy

        end brep

create_bag [T] ( ) returns (bag[T])
        return (brep[T]{sz := 0, els := array_create[T]( )})
        end create_bag

This module exports an implementation of the "create_bag" routine, which enables users to obtain bag objects implemented by the "brep" class. Note the use of the class constructor (7.4) in the "copy" method and "create_bag" to obtain a new bag object.

next up previous contents index
Next: Inheritance Up: Implementations Previous: Parameterized Implementations