9 POLYMORPHISM AND DYNAMIC BINDING 

Inheritance is not just a module combination and enrichment mechanism. It also enables the
definition of flexible entities that may become attached to objects of various forms at run
time, a property known as polymorphism. 

This remarkable facility must be reconciled with static typing. The language convention is
simple: an assignment of the form a := b is permitted not only if a and b are of the same
type, but more generally if a and b are of reference types A and B, based on classes A and
B such that B is a descendant of A. 

This corresponds to the intuitive idea that a value of a more specialized type may be
assigned to an entity of a less specialized type -- but not the reverse. (As an analogy,
consider that if you request vegetables, getting green vegetables is fine, but if you ask for
green vegetables, receiving a dish labeled just "vegetables" is not acceptable, as it could
include, say, carrots.) 

What makes this possibility particularly powerful is the complementary facility: feature
redefinition. A class may redefine some or all of the features which it inherits from its
parents. For an attribute or function, the redefinition may affect the type, replacing the
original by a descendant; for a routine it may also affect the implementation, replacing the
original's routine body by a new one. 

Assume for example a class POLYGON, describing polygons, whose features include an
array of points representing the vertices and a function perimeter which computes a
polygon's perimeter by summing the successive distances between adjacent vertices. An
heir of POLYGON may begin as: 

     class RECTANGLE inherit 

          POLYGON 
               redefine perimeter end 

     feature -- Specific features of rectangles, such as: 

          side1: REAL; side2: REAL; 

          perimeter: REAL is 
                    -- Rectangle-specific version 
               do 
                    Result := 2 * (side1 + side2) 
               end; 

          ... Other RECTANGLE features ... 

Here it is appropriate to redefine perimeter for rectangles as there is a simpler and more
efficient algorithm. Note the explicit redefine subclause (which would come after the
rename if present). 

Other descendants of POLYGON may also have their own redefinitions of perimeter. The
version to use in any call is determined by the run-time form of the target. Consider the
following class fragment: 

          p: POLYGON; r: RECTANGLE; 

          ... 
          !! p; !! r; 
          ... 
          if c then 
               p := r 
          end; 

          print (p.perimeter) 

The polymorphic assignment p := r is valid because of the above rule. If condition c is false,
p will be attached to an object of type POLYGON for the computation of p.perimeter,
which will thus use the polygon algorithm. In the opposite case, however, p will be attached
to a rectangle; then the computation will use the version redefined for RECTANGLE. This
is known as dynamic binding. 

Dynamic binding provides a high degree of flexibility. The advantage for clients is the ability
to request an operation (such as perimeter computation) without explicitly selecting one of
its variants; the choice only occurs at run-time. This is essential in large systems, where
many variants may be available; each component must be protected against changes in
other components. 

This technique is particularly attractive when compared to its closest equivalent in traditional
approaches. In Pascal or Ada, you would need records with variant components, and case
instructions to discriminate between variants. This means that every client must know about
every possible case, and that any extension may invalidate a large body of existing software.

These techniques support a development mode in which every module is open and
incremental. When you want to reuse an existing class but need to adapt it to a new context,
you can always define a new descendant of that class (with new features, redefined ones, or
both) without any change to the original. This facility is of great importance in software
development, an activity which -- whether by design or by circumstance -- is invariably
incremental. 

The power of polymorphism and dynamic binding demands adequate controls. First, feature
redefinition, as seen above, is explicit. Second, because the language is typed, a compiler
can check statically whether a feature application a.f is correct. In contrast, dynamically
typed object-oriented languages defer checks until run-time and hope for the best: if an
object "sends a message" to another (that is to say, calls one of its routines) one just expects
that the corresponding class, or one of its ancestors, will happen to include an appropriate
routine; if not, a run-time error will occur. Such errors will not happen during the execution
of a type-checked Eiffel system. 

In other words, the language reconciles dynamic binding with static typing. Dynamic
binding guarantees that whenever more than one version of a routine is applicable the right
version (the one most directly adapted to the target object) will be selected. Static typing
means that the compiler makes sure there is at least one such version. 

This policy also yields an important performance benefit: in contrast with the costly run-time
searches that may be needed with dynamic typing (since a requested routine may not be
defined in the class of the target object but inherited from a possibly remote ancestor), the
EiffelBench implementation always finds the appropriate routine in constant-bounded time. 

Assertions provide a further mechanism for controlling the power of redefinition. In the
absence of specific precautions, redefinition may be dangerous: how can a client be sure
that evaluation of p.perimeter will not in some cases return, say, the area? Preconditions
and postconditions provide the answer by limiting the amount of freedom granted to
eventual redefiners. The rule is that any redefined version must satisfy a weaker or equal
precondition and ensure a stronger or equal postcondition than in the original. In other
words, it must stay within the semantic boundaries set by the original assertions. 

The rules on redefinition and assertions are part of the Design by Contract theory, where
redefinition and dynamic binding introduce subcontracting. POLYGON, for example,
subcontracts the implementation of perimeter to RECTANGLE when applied to any entity
that is attached at run-time to a rectangle object. An honest subcontractor is bound to
honor the contract accepted by the prime contractor. This means that it may not impose
stronger requirements on the clients, but may accept more general requests, so that the
precondition may be weaker; and that it must achieve at least as much as promised by the
prime contractor, but may achieve more, so that the postcondition may be stronger. 
