18.2 Multiple inheritance and slots
For the most part, using multiple inheritance does not present special problems in using slots. Recall that a class inherits all the slots of its superclasses. A subclass can also add slots of its own, but it cannot remove or replace any slots defined by its superclasses. A slot can appear only once in a class and in all that class's superclasses. Thus, a class's slots are the union of its slots and those of all its superclasses, and duplicate slot definitions are not permitted. This rule holds, regardless of whether a class has one direct superclass or more than one.
Comparison with C++: In C++, a data member of the same name can appear in both a base class and a derived class. The name in the scope of the derived class hides the same name in the base class, but the base class slot can be accessed by qualifying its name. All access to Dylan class slots is through getter and setter methods, which are similar to C++ virtual functions. In Dylan, you can override access to an inherited slot by defining a getter or setter method specialized on the subclass (derived class). For more information on inheritance in C++, see Section B.2, page 390. |
There are ways, however, in which subclasses and superclasses can have distinct effects on the same slot. One way is by providing default values for the slot. Even though duplicate slots are not permitted, a class can provide its own default value for a slot that it inherits from a superclass. The subclass can provide this default by supplying in its class definition an inherited slot option that includes an init-value: or init-function: specification, or an init expression.
Suppose that more than one class defines a default value for the same slot. Which default takes precedence? When each class has only one direct superclass, the answer is easy: the default value provided by the most specific class takes precedence. A default value for a subclass overrides a default value for a superclass.
But what if a class has more than one direct superclass, and each superclass provides a different default value for the same slot? Imagine, for example, that our <vehicle> class had a slot named fuel-remaining, and our <ground-vehicle> and <flying-vehicle> classes each had a different default value for the fuel-remaining slot, which they inherit from the common superclass <vehicle>:
define abstract class <vehicle> (<physical-object>) slot fuel-remaining :: <integer>; ... end class <vehicle>; define abstract class <ground-vehicle> (<vehicle>) inherited-slot fuel-remaining, init-value: 30; ... end class <ground-vehicle>; define abstract class <flying-vehicle> (<vehicle>) inherited-slot fuel-remaining, init-value: 3000; ... end class <flying-vehicle>; define abstract class <aircraft> (<flying-vehicle>, <ground-vehicle>) ... end class <aircraft>;
Now neither the class <ground-vehicle> nor the class <flying-vehicle> is more specific than the other with respect to <aircraft>. So when we create an instance of <aircraft> that has both <ground-vehicle> and <flying-vehicle> as direct superclasses, what is the default initial value for the fuel-remaining slot: 30 or 3000?
To answer this question, Dylan needs an additional way of ordering classes, called a class precedence list. In Section 18.3, we describe how Dylan constructs the class precedence list. The short answer to our question about default initial slot values is that Dylan uses the default value provided by the class that appears earlier in the class precedence list.
We shall see that the class precedence list is also important for method dispatch in the presence of multiple inheritance. Suppose, for example, that we had defined two getter or two setter methods for the fuel-remaining slot: one specialized on the <flying-vehicle> class, and the other specialized on the <ground-vehicle> class. Which method would be selected to get or set the slot value of an instance of <aircraft>? We return to the issue of method dispatch after we see how Dylan constructs the class precedence list.




