A class definition must indicate explicitly that it is available for
subclassing by explicitly providing an interface to its subclasses:

<provides> -> provides <idn_list>
<hides> -> hides <idn_list>

The provides clause lists private methods, routines, and
makers (10.5.2) implemented in the class's module that are available to
subclasses; it also lists any public methods where the signature given
in the class differs from that given in the type and the class
definer wishes this information to be visible to definers of
subclasses. Some makers must be provided,
since otherwise subclasses will have no way to create new objects of
their own; if no maker for the class is listed, there will be a
compile-time error. The hides clause lists public methods that
are not available to subclasses.

The final idn in the maker_def must match that given
in the maker_interface. The type_designator in the makes clause must denote a class type implemented in the maker's module;
we say this is the maker's class.

The object being initialized by a maker is created by the Theta
runtime before the maker is called; its class is some subclass of the
maker's class, and the new object is an implicit argument of the
maker. The maker fills in its fields using a "make" statement;
the new object can be referred to explicitly by the name "self",
but only within the optional body of the "make"
statement (8.17). The usual scoping rules for "self"
apply: the instance variables of "self" can also be directly named as variables
within the body of the "make" (7.5).

A maker cannot contain a return statement. Normal termination
of a make statement causes its execution to terminate normally.
If the maker reaches the end of its body, it will terminate with the
exception "failure(no return results)".

A maker can be called only within a make
statement (8.17) or a class
constructor (7.4) within some subclass of the
maker's class; the maker cannot be called within its own class. It is
used in the subclass to initialize the instance variables inherited
from a superclass, and thus its use is limited to modules implementing
subclasses of its class. It must be listed in the provides
list of its class.

The type_designator names the superclass.
The inherits clause makes the superclass name and the
names of provided methods and
routines visible to code in the subclass's module.

The inheritance hierarchy is independent of the type hierarchy.
Therefore the type implemented by the superclass might not be a
supertype of the type implemented by the subclass. For example, it
might be convenient to implement stacks by inheriting from a class
that implements lists even though stack is not a subtype of list.
Note also that either class might not implement a type.

The instance variables declared in the subclass are in addition to
those of the superclass: objects of the subclass have all the inherited instance variables of the superclass as well as those of
the subclass. However, the inherited variables cannot be accessed
directly in the subclass; it can access them only
by calling the superclass methods.

Objects of the subclass have all the methods of the
superclass, although the hidden methods aren't visible within
the subclass.
Visible superclass methods can
be renamed: the second idn in a renames clause gives the
name of the method in the superclass; the first gives the new name.
For example,

C = class for T inherits D {foo for bar}

renames D's "bar" method to "foo". All superclass methods not
mentioned in a renaming clause retain their original names. The
effect of the renamings is that the superclass appears to have been
rewritten with the names needed in the subclass.

Visible superclass methods can be inherited by the subclass: this is
accomplished by simply not giving an implementation of a method of
that name.
Subclass methods can also be implemented explicitly. If such a method
has the same name as a visible superclass method, the new implementation overrides the associated superclass method. In such a case, the
subclass object has both the overridden method and the new method; the
overridden method is a private method and it can be named using the
the special form "" idn.
For example, if the subclass overrides visible superclass method "m", the
overriding definition is named "m", and the overridden method is named
"" [tex2html_wrap2957]m. Thus code in the subclass and its module can
continue to call the overridden method using the "" form.

Methods overridden by a subclass can affect the behavior of superclass
methods. If a superclass method calls a method "n" that has been
overridden by the subclass, the implementation of "n" provided by
the subclass will run, not the implementation provided by the
superclass. For example, consider superclass method

m ( ) returns (int)
return (self.n())
end m

and suppose that "n" is visible and has been overridden in the subclass.
When "m" is called on
an object of the subclass, its call of "n" goes to the overriding
definition. Therefore, we require that the overriding definition have
a signature that is a subtype of the signature of the method it
overrides.

[usage1437]

Within a class-constructor for the subclass, or within a make
statement of a maker for the subclass, a maker of the superclass must
be called to initialize the superclass fields of the new object. For
example, inside a parameterized maker "make_stack"[T], we might have a
make statement:

make { ... ; make_list[T](...) }

where stack is being implemented as a subclass of list and "make_list"
is a maker provided for list.

A subclass type is not a subtype of the superclass type, nor is
it a subtype of the type implemented by the superclass, unless the
type implemented by the subclass is a subtype of the type implemented
by the superclass. With one exception, ordinary type checking
restrictions apply to subclass objects, e.g., a subclass object cannot
be assigned to a variable whose type is the superclass type. The
exception is that the code of a subclass and its module can call the
methods and routines provided by its superclass passing in subclass
objects as arguments in positions where an object of the superclass
type, or of the type implemented by the superclass, is required.

A subclass can use its hides clause to avoid
exporting inherited methods to its subclasses
and can use its provides clause to export methods,
and also routines and makers implemented in its module.
However, it cannot use the "" notation to name
methods in the provides clause, and it cannot provide any
methods or routines that have the superclass type in their signatures
(since its superclass is not visible to its subclasses).

A superclass should guarantee that subclasses cannot interfere with
the correct functioning of its code and the code in its module.
This can be accomplished by care in
implementing the module and by using the provides and hides clauses appropriately.
Below we discuss two problems that must be avoided: masquerading,
and propagation of bad information.

None of the
methods or routines that the superclass provides to its subclasses
should create an alias for one of the superclass objects.
If such a method or routine were provided, it could be used
by the code in the subclass and its module to
cause subclass objects to masquerade as superclass objects.
Masquerading is bad because subclass objects may behave differently than
superclass objects. For example, if the "bag
copy" method were implemented:

copy ( ) returns (bag[T])
return (self)
end copy

it would create an alias for self. If "copy" were provided to
subclasses of "bag", "return(x.copy())" within the subclass, where x
is an object of the subclass, will cause a subclass object to appear
to be a bag object, even though it might not behave like one.

The second problem - propagation of bad information -
occurs only if a provided method, routine, or
maker violates the superclass rep invariant, and is easily avoided by
not providing such violators. However, providing violators
is sometimes useful, and they are bad only in combination with
propagators: methods, routines, and makers that perform incorrectly
if the superclass rep invariant doesn't hold for some object they
access. For example, suppose the "bag copy" method simply copied its
instance variables; if the rep invariant weren't satisfied, the result
would be a "bag" object that did not satisfy the rep invariant.
So, a class that provides violators should not also
provide propagator methods, routines, and makers to its subclasses.
In addition its module should not
export to its users any propagator routines, and its other classes should
not export
any propagator methods.

This section illustrates the use of inheritance by means of a
simple example.

Suppose we want to implement the "stack" abstraction specified in
Section (9.4) as a subclass of the "bag" implementation
given in Section (10.4.3). To do so, at the least we must
provide some makers for "stack" to use: a maker for initializing an
empty "stack", and also some way of initializing the "stack" returned
by the "copy" method. We must also consider how to implement the
"top" method.

Here is one solution to these problems: "bag" provides its subclasses
with access to the array that contains its elements, e.g., by
providing a "get_els" method, which returns the "els" component of a
bag. However, this method is effectively a violator, since it
allows the subclass to modify the array, thus violating the rep
invariant. Therefore care must be taken to not provide any
propagators.

The "mk_copy" maker ensures the rep invariant by setting the "sz"
field of the new object appropriately. The "copy" method is hidden
since it is a propagator. If the bag implementation had not exported
a violator, it would not be necessary to hide the "copy" method.

Here is an implementation of "stack" that demonstrates the use of
inheritance.

For this implementation, it is not necessary to define any additional
instance variables. Note that the "stack" implementation is dependent
on the details of the "bag" implementation, e.g., that "put" adds the
new element to the high end of the array and "get" removes the newest
element.

In this example, the implemented types ("stack" and "bag") are in a
subtype relationship that mirrors the inheritance relationship of
the implementing classes, as shown in Figure 10.1. This means
that "srep" indirectly provides another implementation of "bag".
However, Theta does not require that such a relationship exists; another
class could inherit from "brep" without implementing a subtype of
"bag".

[figure1474]
Figure 10.1: The subtype and inheritance relationships of srep