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:
.
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 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]
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
[]
We will use type expressions of the form to denote
that the expression e is of type .
\oopindex{conformance}
The conformance rule for arbitrary expressions may now be stated as follows.
[D] $(Conformance) if e:%t' and %t' <= %t then e:%ta:[2..5]a:[1..6][i..j]f:%s -> %t%s%tf'%s' -> %t'
The difficulty of applying this rule is brought about by the contravariance
between the domain types, namely whereas .
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 }}a_i:%t_ii=1,...,na_i:%t_i'%t_i' <= %t_ivehicle = {{ age : integer, speed : integer }}machine = {{ age : integer, fuel : string }}car = {{ age : integer, speed : integer, fuel : string }}