The simplest case is when a new class inherits some properties (slots and methods) from a single superclass. That superclass may, in turn, be defined in terms of its superclass, etc. The new class, its superclass, its superclass's superclass (if any) and so on are all ancestors of the new class.
The definition of a class with a single superclass begins with a class/1
directive of the form
:- class ClassName = [SlotDef, ...] + SuperClass.
where the list of SlotDef descriptions may be empty. In that case, the definition can simplified to
:- class ClassName = SuperClass.
The class SuperClass must be a defined class when this definition is given.
In SICStus Objects, a subclass inherits all the slots of its superclass. And, by default, it inherits all the methods of its superclass. The remainder of this section describes what the programmer can do to control this inheritance.
A class's slots are a combination of those explicitly defined in its slot description list and the slots it inherits from its superclass. In SICStus Objects, a class inherits all the slots of its superclass. It follows that a class inherits all the slots of all its ancestors.
The programmer's control over inheritance of slots is limited. It is not possible to rename an inherited slot, nor is it possible to change its type, unless it is a class slot. It is possible to change a slot's initial value. And, it is possible to effectively change a slot's visibility.
To change the initial value or the type (when allowed) of a slot, include a new SlotDef in the list of slot descriptions for the class, with the same slot name and a new type or initial value. The type of a class slot can only be changed to a subclass of the type of the superclass's slot. The new initial value must still be a constant of the appropriate type.
The named_point
class, defined earlier, could have better been defined
from the point class, which began as follows:
:- class point = [public x:float=0, public y:float=0].
The definition of the named_point
class would then begin with
:- class named_point = [public name:atom, public x:float=1.0] + point.
This named_point
class has public slots named name
, x
and
y
, with the same types and initial values as the earlier
named_point
definition, which did not use inheritance. This
named_point
class also inherits all the methods of the point
class, which saves us from having to write them again (and maintain them).
A slot that was private or protected in a superclass may be defined as
public. This will cause get and put methods to be generated in the
subclass. A slot that was public in a superclass may be defined as
protected or private, but this does not prevent it from inheriting the get and
put methods of the superclass. For that, the uninherit/1
directive, defined below, is
needed.
In SICStus Objects, by default, a class inherits all the methods of its superclass. The programmer has more control over the inheritance of methods than the inheritance of slots, however. In particular, methods can be uninherited and they can be redefined.
To prevent a method from being inherited, use the uninherit/1
directive. For example, suppose that the class point
is defined as
before. That is, its definition begins as follows:
:- class point = [public x:float=0, public y:float=0].
Because both slots are public, a put method is automatically generated for each, which allows their values to be changed.
The definition of a new class fixed_point
might begin as follows:
:- class fixed_point = point. :- uninherit point << (x/l), point << (y/l). Self <- create(X, Y) :- store_slot(x, X), store_slot(y, Y).
The parentheses are necessary because of the precedences of the ‘<<’ and ‘/’ operators.
Because the put methods from point
are not inherited, no instance of
the fixed_point
class can change its x
and y
values once
created—unless the class definition contains another method for doing
so. The get methods are inherited from point
, however.
To redefine a method, simply include method clauses for its message within a class's definition. The new method clauses replace, or shadow, the inherited method clauses for this class.
Another way to prevent the x
and y
slots of the
fixed_point
class from being modified would be to shadow the put
methods. For example, they might be redefined as
Self << x(_) :- format(user_error, "cannot modify x slot value.~n.", []), fail. Self << y(_) :- format(user_error, "cannot modify y slot value.~n", []), fail.
Now attempts to modify the x
or y
values of a fixed point
object generate a specific error message and fail. A more complicated
version would raise an appropriate exception.
Even when a superclass's method is shadowed or uninherited, it is possible to use the superclass's method inside a method clause for the new class. This makes it possible to define a “wrapper” for the superclass's method, which invokes the superclass's method without having to duplicate its code. This technique works with all message types.
Sending a message to a superclass is done with a command of the form
super
MessageOp Message
where MessageOp is one of the message operators (‘<<’, ‘>>’ or ‘<-’) and Message is a message defined for the superclass. A generalization of this syntax may be used to specify which superclass to send the message to. This is discussed in obj-inh-mih.
Sending a message to a class's superclass can only be done within a message clause.