18.4.1 Class precedence and method dispatch
To see how this problem for method dispatch can arise, we return to our airport example. Recall that we now have two slots representing vehicle cruising speed: ground-cruising-speed for <ground-vehicle> and flying-cruising-speed for <flying-vehicle>. Let's define a generic function, say-cruising-speed, to report the applicable cruising speed for each class:
define generic say-cruising-speed (vehicle :: <vehicle>);
// Method 1
define method say-cruising-speed (vehicle :: <flying-vehicle>)
format-out("Flying cruising speed: %d\n", vehicle.flying-cruising-speed);
end method say-cruising-speed;
// Method 2
define method say-cruising-speed (vehicle :: <ground-vehicle>)
format-out("Ground cruising speed: %d\n", vehicle.ground-cruising-speed);
end method say-cruising-speed;
// Method 3
define method say-cruising-speed (vehicle :: <vehicle>)
format-out("No cruising speed defined for type <vehicle>\n");
end method say-cruising-speed;
Now, suppose that we call say-cruising-speed on an instance of <B707>. Which method does Dylan call? All three methods are applicable. Both method 1 and method 2 are more specific than is method 3. But Dylan cannot order methods 1 and 2 by specificity.
In this case, Dylan consults the class precedence list for the class of the argument. In our example, the class of the argument is <B707>. The <flying-vehicle> class takes precedence over the <ground-vehicle> class, because <flying-vehicle> precedes <ground-vehicle> in the list of direct superclasses for <aircraft>. Dylan calls method 1, which produces the following output:
Flying cruising speed: 368
Note that, if we had happened to list <ground-vehicle> before <flying-vehicle> in the list of direct superclasses for <aircraft>, Dylan would have called method 2, and we would have seen the following output:
Ground cruising speed: 45
In defining classes of aircraft, we did not intend for <flying-vehicle> characteristics to override <ground-vehicle> characteristics. But for method dispatch to work in the presence of multiple inheritance, Dylan must order subclasses and superclasses whenever it can.
How can we change our example to make <flying-vehicle> behavior add to, rather than override, <ground-vehicle> behavior? By using next-method in our say-cruising-speed methods for <flying-vehicle> and <ground-vehicle>, we can report all applicable kinds of cruising speed for any combination of either or both of those classes. To make this behavior work, we also change the say-cruising-speed method for <vehicle>, which will always be called last, to have no effect:
// Method 1
define method say-cruising-speed (vehicle :: <flying-vehicle>)
format-out("Flying cruising speed: %d\n", vehicle.flying-cruising-speed);
next-method();
end method say-cruising-speed;
// Method 2
define method say-cruising-speed (vehicle :: <ground-vehicle>)
format-out("Ground cruising speed: %d\n", vehicle.ground-cruising-speed);
next-method();
end method say-cruising-speed;
// Method 3
define method say-cruising-speed (vehicle :: <vehicle>)
end method say-cruising-speed;
Recall that, when Dylan decides which method to call, the result is a list of methods, sorted by specificity. When say-cruising-speed is called on an instance of <B707>, the list of methods is sorted in the following order: method 1, method 2, method 3. Dylan calls method 1. Then, as a result of the call to next-method in method 1, Dylan calls method 2. Finally, as a result of the call to next-method in method 1, Dylan calls method 3. The output we see is as follows:
Flying cruising speed: 368 Ground cruising speed: 45
Note that, if we called say-cruising-speed on an instance of <fuel-truck>, we would see the following output:
Ground cruising speed: 25




