As promised, here is the other half of inheritance, subtyping. A subtyping clause ('<' followed by type specifiers) indicates that the abstract signatures of all types listed in the subtyping clause are included in the interface of the type being defined. In the example, the subtyping clause is
abstract class $SHIPPING_CRATE < $CRATE is ... |
The interface of an abstract type consists of any explicitly specified signatures along with those introduced by the subtyping clause.
Every type is automatically a subtype of $OB
Only abstract types can be mentioned in the subtyping clause
When a subtyping clause is used in a partial class, it enforces the basic subtyping rule using the stub routine.
There must be no cycle of abstract types such that each appears in the subtype list of the next, ignoring the values of any type parameters but not their number.
A subtyping clause may not refer to SAME.
SAME is only permitted as a return type or on out arguments in abstract class signatures.
We frequently refer to the Sather type graph, which is a graph whose nodes represent Sather types and whose edges represent subtyping relationships between sather types. Subtyping clauses introduce edges into the type graph. There is an edge in the type graph from each type in the subtyping clause to the type being defined. The type graph is acyclic, and may be viewed as a tree with cross edges (the root of the tree is $OB, which is an implicit supertype of all other types).
abstract class $TRANSPORT is ... abstract class $FAST is ... abstract class $ROAD_TRANSPORT < $TRANSPORT is ... abstract class $AIR_TRANSPORT < $TRANSPORT, $FAST is ... class CAR < $ROAD_TRANSPORT is ... class DC10 < $AIR_TRANSPORT is ... |
Since it is never possible to subtype from a concrete class (a reference, immutable or external class), these classes, CAR and DC10 form the leaf nodes of the type graph.
Once we have introduced a typing relationship between a parent and a child class, we can use a variable of the type of the parent class to hold an object with the type of the child. Sather supports dynamic dispatch - when a function is called on a variable of an abstract type, it will be dispatched to the type of the object actually held by the variable. Thus, subtyping provides polymorphism.
To illustrate the use of dispatching, let us consider a system in which variables denote abstract employees which can be either MANAGER or EMPLOYEE objects. Recall the defintions of manager and employee
class EMPLOYEE < $EMPLOYEE is ... -- Employee, as defined earlier class MANAGER < $EMPLOYEE is ... -- Manager as defined earlier |
For the definition of these classes, see unnamedlink
The above defintions can then be used to write code that deals with any employee, regardless of whether it is a manager or not
class TESTEMPLOYEE is main is employees:ARRAY{$EMPLOYEE} := #ARRAY{$EMPLOYEE}(3); -- employees is a 3 element array of employees i:INT := 0; wage:INT := 0; loop until!(i = employees.size); emp:$EMPLOYEE := employees[i]; emp_wage:INT := emp.wage; -- emp.wage is a dispatched call on "'age' wage := wage + emp_wage; end; #OUT + wage + "\n"; end; end; |
The main program shows that we can create an array that holds either regular employees or managers. We can then perform any action on this array that is applicable to both types of employees. The wage routine is said to be dispatched. At compile time, we don't know which wage routine will be called. At run time, the actual class of the object held by the emp variable is determined and the wage routine in that class is called.