10.13.3.1 Single Inheritance

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.

Class Definitions

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.

Slots

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.

Methods

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.

Send Super

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.


Send feedback on this subject.