Inheritance

\oopindex{inheritance} The popularity of the object oriented style of programming is to a large extent due to the possibility of sharing object (and class) specifications by means of inheritance. Objects in themselves provide the means to encapsulate the implementation of behavior and to regulate the interactions between objects by defining an external interface that establishes the responsibilities of the object. In addition to the clients that request a service of an object by invoking one of the methods listed in the external method interface, inheritance introduces a new category of clients that make use of the functionality of (classes of) objects by sharing functional resources of these objects, that is instance variables and methods. \oopindex{modeling} Inheritance may play a crucial role in object oriented modeling since it allows to factorize the properties common to a collection of classes in a common ancestor class. Because of this feature abstract classes may be specified to define the external interface of a collection of classes. The subclasses of an abstract class may then refine the definition provided by the abstract class by providing an implementation. From a software engineering perspective, inheritance promotes the reuse of software since when a class only captures part of the required functionality it may -- using inheritance -- easily be refined into a class that does capture all the functionality required.

Specialization hierarchies

\oopindex{specialization} In its most simple form, the inheritance mechanism provides a way of sharing common attributes, and thus allows to create a specialization hierarchy among a number of concepts. An example of such a specialization hierarchy is given in the tree below that depicts the relation between a variety of fruit. \begin{center} .so fruit \end{center} Since oranges are not the only fruit, we may encounter in this hierarchy a subtree that specifies some of the varieties of apples. As a common attribute of all the items in the tree we may think of the property of being edible. Specializations will occur with respect to the texture of their skin and the place of growth for example. The most common interpretation of this kind of taxonomies is given by a predicate logic rendering of the relations expressed by the specialization tree. The hierarchy depicted above, for example, states that all apples are (a kind of) fruit, or in a predicate logic formula: \A x. apple(x) -> fruit(x). Semantically, each node in the tree corresponds to a set of individuals (elements of a domain of discourse). This set is exactly the set described by the information provided by that particular node. In a specialization tree, each descendant of a node provides more specific information and thus restricts the number of individuals to which the description applies. In other words, taken as sets of individuals, the relation apple \< fruit holds.

Multiple inheritance

\oopindex{multiple inheritance} Instead of one ancestor, as in the specification hierarchy above, a concept may as well have multiple parents from which it inherits. For instance, if we have a concept edible then we may make apple inherit from edible to express that all apples are edible. As another example, if we have an object (type) machine with attributes age and fuel and an object type vehicle with attribute age and speed then we may create the object type car with attributes age, speed and fuel by inheriting from both machine and vehicle, as pictured below. \begin{center} .so car \end{center} The meaning of the concept car is the set of individuals that is both a machine and a vehicle, in other words the cross-section of the sets corresponding to machine and vehicle. See  [Ca84].

Conformance

\oopindex{conformance} Ideally, the inheritance relation in object oriented programming languages conforms to the notion of refining a description of a concept by providing more information. In that case we have a substitution property that states when a concept conforms to another concept: \begin{quotation} (Substitution) {\em whenever we may use an instance of a concept we may also use an instance of a refinement of that concept.} \end{quotation} This holds also in the case of multiple inheritance. For instance, if I am asked for a vehicle then I may hand over my car. \oopindex{polymorphism} Technically, conformance may be checked by identifying concepts or classes with types. Regarding concepts as types, we speak of polymorphism since the inherited concepts may be taken to be subtypes of the original concept. \nop{ In practice this appears to be an unrealistic goal, since many applications of inheritance violate this principle of refinement. } However, conflict may arise when properties are inherited that contradict each other. A famous example, illustrating the possibility of ambiguity in property inheritance systems, is the so-called Nixon-diamond. \begin{center} .so nixon \end{center} The inheritance diamond above states that Nixon is both a Quaker and a Republican. Knowing that Quakers are notorious pacifists and Republicans equally notorious non-pacifists, the question arises whether Nixon is a pacifist or a non-pacifist. Notorious, no doubt. With regard to the diamond, evidently the logical theory expressed by the inheritance graph is clearly inconsistent. C.f.  [To86]. If all the properties of the inherited concepts are preserved in the inheriting concept we say that the inheritance relation is monotonic. Otherwise, we say that the inheritance relation is non-monotonic. As observed in  [WZ88], incremental system evolution often turns out to be non-monotonic, in practice. Non-monotonicity occurs when either exceptions or overridings are used to effect the desired behavior. In these cases we can no longer speak of behavioral refinement to characterize the inheritance relation employed.

Polymorphism

\oopindex{polymorphism} To decide on whether an object type conforms to another object type we need to have a notion of type and subtype, since then we may replace conforms to by being a subtype. \oopindex{subtyping} Pragmatically, we may regard a class inheriting from a class as a subtype of the type defined by the class, since the meaning of a class is operationally defined by the method lookup procedure employed by instances thereof. See  [WZ88]. However, viewing classes as types is really overspecifying the notion of conformance and moreover such a view does not allow static type checking, since type errors are only dynamically detected as the result of a failing method lookup. \oopindex{dynamic method lookup} The procedure for dynamic method lookup (in the case of single inheritance) may be phrased recursively as follows. \yprog{lookup}{
  procedure lookup(method, class)
       if method = localmethod then do localaction
       elsif inheritedclass = nil then undefined
       else lookup(method, inheritedclass)
  
} Multiple inheritance gives rise to more complicated lookup algorithms. See  [DH88]. With regard to polymorphism, whenever a method is defined for a superclass then no type error will occur when calling the method for an instance of a subclass since all methods of the superclass are inherited by the subclass. They may however be redefined, which involves the risk of specifying contradictory behavior that (intuitively) does not conform to the intended behavior of the inherited class. \oopindex{dynamic binding} \oopindex{virtual functions} The dynamic binding that occurs with virtual functions, for instance in C++, resembles the method lookup procedure sketched above. Conformance to a supertype however is statically checked in C++. Also, in C++ there are ways to use inheritance in a non-standard way, for instance to restrict the functionality of a class of objects. C.f.  [HOB87]. A classical example of a nonstandard application of inheritance is to derive a stack from a double ended queue by disabling the ordinary deque operation (that delivers the first, that is oldest element of the queue). However, from the perspective of types, the relation [D] DQueue <_{subtype} Stack holds, since a double ended queue may be regarded as inheriting all the behavior of a stack while adding a deque operation.

Objects as records

\oopindex{objects as records} In order to grasp the subtype relation between object classes we need to introduce a more formal notion of object types. To establish the type of an object, we may regard an object as a record containing attributes and functions that are accessible by labels. The notions employed here are due to  [Ca84]. An example of a record with values is the record [] { a = 1, b = true, c = "hello" } We will use type expressions of the form e:%t to denote that the expression e is of type %t. \oopindex{conformance} The conformance rule for arbitrary expressions may now be stated as follows. [D] $(Conformance) if e:%t' and %t' <= %t then e:%t The rule expresses that an expression may always be regarded as being of an appropriate supertype. For simple attributes a, say of type integer or subranges thereof, this property is easy to establish. For instance, when a:[2..5] then also a:[1..6], taking [i..j] to be the interval ranging from i to j. The subtype relation for simple types corresponds to the subset relation with respect to the sets of individuals denoted by these types. However, for functions f:%s -> %t, where %s is the type of the domain and %t the type of the range of f, it is much more difficult to establish whether a function f' of type %s' -> %t' conforms to (the type of) f. In  [Ca84] the function conformance rule is given by [] $(Functions) if %s <= %s' and %t' <= %t then f':%s' -> %t' <= f:%s -> %t The difficulty of applying this rule is brought about by the contravariance between the domain types, namely %s <= %s' whereas f' <= f. We hope to give a more intuitive understanding of this rule in the next section by exploring the notion of behavioral refinement for functions. In order to define a subtype relation for objects we state the following conformance rule for records. [D $(Records) if %t_1' <= %t_1 and ... and %t_n' <= %t_n then {{ a1:%t_1',...,a_{n+m}:%t_{n+m}' }} <= {{ a1:%t_1,...,a_n:%t_n }} D] In other words, a record type is a subtype of another record type if for each of the field a_i:%t_i (for i=1,...,n) there is a corresponding field a_i:%t_i' for which %t_i' <= %t_i. The subtype may, however, contain additional fields. As an example, defining the record types [D vehicle = {{ age : integer, speed : integer }} machine = {{ age : integer, fuel : string }} D] we may establish that the record type car defined by [D car = {{ age : integer, speed : integer, fuel : string }} D] is a subtype of both vehicle and machine. We now have a (syntactic) notion of types that enables us to decide whether an object (type) conforms to -- is a subtype of -- another object (type).\ftn{ \oopindex{object types} In this treatment, we have not paid any attention to the recursive structure of object types. For a treatment of these aspects see  [CW85] and  [CoHC90]. } In the next section we will provide a more intuitive notion of (behavioral) refinement based on the notion of subtype conformance introduced here. .so refinement