[[Property:title|ET: Inheritance]] [[Property:weight|-7]] [[Property:uuid|c90e6ee3-b39d-48e5-2321-a34e12fd5327]] Inheritance is a powerful and attractive technique. A look at either the practice or literature shows, however, that it is not always well applied. Eiffel has made a particular effort to tame inheritance for the benefit of modelers and software developers. Many of the techniques are original with Eiffel. Paul Dubois has written (comp.lang.python Usenet newsgroup, 23 March 1997): there are two things that [Eiffel] got right that nobody else got right anywhere else: support for design by contract, and multiple inheritance. Everyone should understand these "correct answers" if only to understand how to work around the limitations in other languages. ==Basic inheritance structure== To make a class inherit from another, simply use an `inherit` clause: note ... class D inherit A B ... feature ... This makes `D` an heir of `A`, `B` and any other class listed. Eiffel supports '''multiple''' inheritance: a class may have as many parents as it needs. Later sections ( [[ET: Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] and [[ET: Inheritance#Repeated_inheritance_and_selection|"Repeated inheritance and selection"]] ) will explain how to handle possible conflicts between parent features. {{note|This discussion will rely on the terminology introduced in [[ET: The Static Picture: System Organization|The Static Picture: System Organization]]: descendants of a class are the class itself, its heirs, the heirs of its heirs and so on. Proper descendants exclude the class itself. The reverse notions are ancestors and proper ancestors. }} By default `D` will simply include all the original features of `A`, `B`, ..., to which it may add its own through its `feature` clauses if any. But the inheritance mechanism is more flexible, allowing `D` to adapt the inherited features in many ways. Each parent name -- `A`, `B`, ... in the example -- can be followed by a '''Feature Adaptation''' clause, with subclauses, all optional, introduced by keywords `rename`, `export`, `undefine`, `redefine` and `select`, enabling the author of `D` to make the best use of the inheritance mechanism by tuning the inherited features to the precise needs of `D`. This makes inheritance a principal tool in the Eiffel process, mentioned earlier, of carefully crafting each individual class, like a machine, for the benefit of its clients. The next sections review the various Feature Adaptation subclauses. ==Redefinition== The first form of feature adaptation is the ability to change the implementation of an inherited feature. Assume a class `SAVINGS_ACCOUNT` that specializes the notion of account. It is probably appropriate to define it as an heir to class `ACCOUNT`, to benefit from all the features of `ACCOUNT` still applicable to savings accounts, and to reflect the conceptual relationship between the two types: every savings account, apart from its own specific properties, also "is" an account. But we may need to produce a different effect for procedure `deposit` which, besides recording the deposit and updating the balance, may also need, for example, to update the interest. This example is typical of the form of reuse promoted by inheritance and crucial to effective reusability in software: the case of reuse with adaptation. Traditional forms of reuse are all-or-nothing: either you take a component exactly as it is, or you build your own. Inheritance will get us out of this "reuse or redo" dilemma by allowing us to reuse and redo. The mechanism is feature redefinition: note description: "Savings accounts" class SAVINGS_ACCOUNT inherit ACCOUNT redefine deposit end feature -- Element change deposit (sum: INTEGER) -- Add sum to account. do ... New implementation (see below) ... end ... Other features ... end -- class SAVINGS_ACCOUNT Without the `redefine` subclause, the declaration of `deposit` would be invalid, yielding two features of the same name, the inherited one and the new one. The subclause makes this valid by specifying that the new declaration will override the old one. In a redefinition, the original version -- such as the `ACCOUNT` implementation of `deposit` in this example -- is called the '''precursor''' of the new version. It is common for a redefinition to rely on the precursor's algorithm and add some other actions; the reserved word `Precursor` helps achieve this goal simply. Permitted only in a routine redefinition, it denotes the parent routine being redefined. So here the body of the new `deposit` (called "New implementation" above) could be of the form Precursor (sum) -- Apply ACCOUNT's version of deposit ... Instructions to update the interest ... In the event that a routine has redefined a particular feature from multiple parent, the `Precursor` syntax allows the inclusion of a parent qualification: Precursor {PARENT_X} (args...) -- Apply PARENT_X's version of this feature ... Instructions to update the interest ... Besides changing the implementation of a routine, a redefinition can turn an argument-less function into an attribute; for example a proper descendant of `ACCOUNT` could redefine `deposits_count`, originally a function, as an attribute. The Uniform Access Principle (introduced in [[ET: The Dynamic Structure: Execution Model|The Dynamic Structure: Execution Model]] ) guarantees that the redefinition makes no change for clients, which will continue to use the feature under the form acc.deposits_count ==Polymorphism== The inheritance mechanism is relevant to both roles of classes: module and type. Its application as a mechanism to reuse, adapt and extend features from one class to another, as just seen, covers its role as a '''module extension''' mechanism. But it's also a '''subtyping''' mechanism. To say that `D` is an heir of `A`, or more generally a descendant of `A`, is to express that instances of `D` can be viewed as instances of `A`. '''Polymorphic assignment''' supports this second role. In an assignment `x := y`, the types of `x` and `y` do not have, with inheritance, to be identical; the rule is that the type of `y` must simply '''conform''' to the type of `x`. {{definition|Conformance|A class `D` conforms to a class `A` if and only if it is a descendant of `A` (which includes the case in which `A` and `D` are the same class); if these classes are generic, conformance of `D` `[U]` to `C` `[T]` requires in addition that type `U` conform to type `T` (through the recursive application of the same rules). }} {{note|In addition, it will be shown in the discussion of tuples ([[ET: Other Mechanisms#Tuple_types|"Tuple types"]]), that `TUPLE [X]` conforms to `TUPLE`, `TUPLE [X, Y]` to `TUPLE [X]` and so on. }} So with the inheritance structure that we have seen, the declarations acc: ACCOUNT sav: SAVINGS_ACCOUNT make it valid to write the assignment acc := sav which will assign to `acc` a reference attached (if not void) to a direct instance of type `SAVINGS_ACCOUNT`, not `ACCOUNT`. Such an assignment, where the source and target types are different, is said to be polymorphic. An entity such as `acc`, which as a result of such assignments may become attached at run time to objects of types other than the one declared for it, is itself called a polymorphic entity. For polymorphism to respect the reliability requirements of Eiffel, it must be controlled by the type system and enable static type checking. We certainly do not want an entity of type `ACCOUNT` to become attached to an object of type `DEPOSIT`. Hence the second typing rule: {{rule|name=Type Conformance|text=An assignment `x := y`, or the use of y as actual argument corresponding to the formal argument x in a routine call, is only valid if the type of y conforms to the the type of x. }} The second case listed in the rule is a call such as `target.routine(..., y, ...)` where the routine declaration is of the form `routine (..., x: SOME_TYPE)`. The relationship between `y`, the actual argument in the call, and the corresponding formal argument `x`, is exactly the same as in an assignment `x := y`: not just the type rule, as expressed by Type Conformance (the type of `y` must conform to `SOME_TYPE`), but also the actual run-time effect which, as for assignments, will be either a reference attachment or, for expanded types, a copy. The ability to accept the assignment `x := Void` for `x` of any reference type (see [[ET: The Dynamic Structure: Execution Model#Basic_operations|"Basic operations"]] ) is a consequence of the Type Conformance rule, since `Void` is of type `NONE` which by construction ([[ET: The Static Picture: System Organization#The_global_inheritance_structure|"The global inheritance structure"]] ) conforms to all types. Polymorphism also yields a more precise definition of "instance". A '''direct instance''' of a type `A` is an object created from the exact pattern defined by the declaration of `A` 's base class, with one field for each of the class attributes; you will obtain it through a creation instruction of the form `create x` ..., for `x` of type `A`, or by cloning an existing direct instance. An '''instance''' of `A` is a direct instance of any type conforming to `A`: `A` itself, but also any type based on descendant classes. So an instance of `SAVINGS_ACCOUNT` is also an instance, although not a direct instance, of `ACCOUNT`. A consequence of polymorphism is the ability to define '''polymorphic data structures'''. With a declaration such as accounts: LIST [ACCOUNT] the procedure call `accounts.extend (acc)`, because it uses a procedure `extend` which in this case expects an argument of any type conforming to `ACCOUNT`, will be valid not only if `acc` is of type `ACCOUNT` but also if it is of a descendant type such as `SAVINGS_ACCOUNT`. Successive calls of this kind make it possible to construct a data structure that, at run-time, might contain objects of several types, all conforming to `ACCOUNT`: [[Image:tutorial-10]] [[Image:tutorial-11]] Such polymorphic data structures combine the flexibility and safety of genericity and inheritance. You can make them more or less general by choosing for the actual generic parameter, here `ACCOUNT`, a type higher or lower in the inheritance hierarchy. Static typing is again essential here, prohibiting for example a mistaken insertion of the form `accounts.extend (dep)` where `dep` is of type `DEPOSIT`, which does not conform to `ACCOUNT`. At the higher (most abstract) end of the spectrum, you can produce an unrestrictedly polymorphic data structure `general_list: LIST [ANY]` which makes the call `general_list.extend (x)` valid for any `x`. The price to pay is that retrieving an element from such a structure will yield an object on which the only known applicable operations are the most general ones, valid for all types: assignment, copy, twin, equality comparison and others from `ANY`. The [[#Object test|object test]], studied below, will make it possible to apply more specific operations after checking dynamically that a retrieved object is of the appropriate type. ==Dynamic binding== The complement of polymorphism is dynamic binding, the answer to the question "What version of a feature will be applied in a call whose target is polymorphic?". Consider `acc` is of type `ACCOUNT`. Thanks to polymorphism, an object attached to `acc` may be a direct instance not just of `ACCOUNT` but also of `SAVINGS_ACCOUNT` or other descendants. Some of these descendants, indeed `SAVINGS_ACCOUNT` among them, redefine features such as `deposit`. Then we have to ask what the effect will be for a call of the form acc.deposit (some_value) Dynamic binding is the clearly correct answer: the call will execute the version of `deposit` from the generating class of the object attached to `acc` at run time. If `acc` is attached to a direct instance of `ACCOUNT`, execution will use the original `ACCOUNT` version; if `acc` is attached to a direct instance of `SAVINGS_ACCOUNT`, the call will execute the version redefined in that class. This is a clear correctness requirement. A policy of static binding (as available for example in C++ or Delphi, for non-virtual functions) would take the declaration of `acc` as an `ACCOUNT` literally. But that declaration is only meant to ensure generality, to enable the use of a single entity `acc` in many different cases: what counts at execution time is the object that `acc` represents. Applying the `ACCOUNT` version to a `SAVINGS_ACCOUNT` object would be wrong, possibly leading in particular to objects that violate the invariant of their own generating class (since there is no reason a routine of `ACCOUNT` will preserve the specific invariant of a proper descendant such as `SAVINGS_ACCOUNT`, which it does not even know about). In some cases, the choice between static and dynamic binding does not matter: this is the case for example if a call's target is not polymorphic, or if the feature of the call is redefined nowhere in the system. In such cases the use of static binding permits slightly faster calls (since the feature is known at compile time). This application of static binding should, however, be treated as a '''compiler optimization'''. The EiffelStudio compiler, under its "finalization" mode, which performs extensive optimization, will detect some of these cases and process them accordingly -- unlike approaches that make developers responsible for specifying what should be static and what dynamic (a tedious and error-prone task, especially delicate because a minute change in the software can make a static call, in a far-away module of a large system, suddenly become dynamic). Eiffel programmers don't need to worry about such aspects; they can rely on the semantics of dynamic binding in all cases, with the knowledge that the compiler will apply static binding when safe and desirable. Even in cases that require dynamic binding, the design of Eiffel, in particular the typing rules, enable compilers to make the penalty over the static-binding calls of traditional approaches very small and, most importantly, '''constant-bounded''' : it does not grow with the depth or complexity of the inheritance structure. The discovery in 1985 of a technique for constant-time dynamic binding calls, even in the presence of multiple and repeated inheritance, was the event that gave the green light to the development of Eiffel. Dynamic binding is particularly interesting for polymorphic data structures. If you iterate over the list of accounts of various kinds, `accounts: LIST [ACCOUNT]`, illustrated in the last figure, and at each step let `acc` represent the current list element, you can repeatedly apply acc.deposit (...) to have the appropriate variant of the `deposit` operation triggered for each element. The benefit of such techniques appears clearly if we compare them with the traditional way to address such needs: using multi-branch discriminating instructions of the form if "Account is a savings account " then ... elseif "It is a money market account" then ... elseif ... and so on, or the corresponding `case ... of ..., switch` or `inspect` instructions. Apart from their heaviness and complexity, such solutions cause many components of a software system to rely on the knowledge of the exact set of variants available for a certain notion, such as bank account. Then any addition, change or removal of variants can cause a ripple of changes throughout the architecture. This is one of the majors obstacles to extendibility and reusability in traditional approaches. In contrast, using the combination of inheritance, redefinition, polymorphism and dynamic binding makes it possible to have a '''point of single choice''' -- a unique location in the system which knows the exhaustive list of variants. Every client then manipulates entities of the most general type, `ACCOUNT`, through dynamically bound calls of the form acc.some_account_feature (...) These observations make dynamic binding appear for what it is: not an implementation mechanism, but an '''architectural technique''' that plays a key role (along with information hiding, which it extends, and Design by Contract, to which it is linked through the assertion redefinition rules seen below) in providing the modular system architectures of Eiffel, the basis for the method's approach to reusability and extendibility. These properties apply as early as analysis and modeling, and continue to be useful throughout the subsequent steps. ==Deferred features and classes== The examples of dynamic binding seen so far assumed that all classes were fully implemented, and dynamically bound features had a version in every relevant class, including the most general ones such as `ACCOUNT`. It is also useful to define classes that leave the implementation of some of their features entirely to proper descendants. Such an abstract class is known as `deferred`; so are its unimplemented features. The reverse of deferred is effective, meaning fully implemented. `LIST` is a typical example of deferred class. As it describes the general notion of list, it should not favor any particular implementation; that will be the task of its effective descendants, such as `LINKED_LIST` (linked implementation), `TWO_WAY_LIST` (linked both ways) `ARRAYED_LIST, ` (implementation by an array), all effective, and all indeed to be found in EiffelBase. At the level of the deferred class `LIST`, some features such as `extend` (add an item at the end of the list) will have no implementation and hence will be declared as deferred. Here is the corresponding form, illustrating the syntax for both deferred classes and their deferred features: note description: "[ Sequential finite lists, without a commitment to a representation. ]" deferred class LIST [G] feature -- Access count: INTEGER -- Number of items in list do ... See below; this feature can be effective ... end feature -- Element change extend (x: G) -- Add `x' at end of list. require space_available: not full deferred ensure one_more: count = old count + 1 end ... Other feature declarations and invariants ... end -- class LIST A deferred feature (considered to be a routine, although it can yield an attribute in a proper descendant) has the single keyword `deferred` in lieu of the `do` ''Instructions'' clause of an effective routine. A deferred class -- defined as a class that has at least one deferred feature -- must be introduced by `deferred class` instead of just `class`. As the example of `extend` shows, a deferred feature, although it has no implementation, can be equipped with assertions. They will be binding on implementations in descendants, in a way to be explained below. Deferred classes do not have to be fully deferred. They may contain some effective features along with their deferred ones. Here, for example, we may express `count` as a function: count: INTEGER -- Number of items in list do from start until after loop Result := Result + 1 forth end end This implementation relies on the loop construct described below ( `from` introduces the loop initialization) and on a set of deferred features of the class which allow traversal of a list based on moving a fictitious cursor: `start` to bring the cursor to the first element if any, `after` to find out whether all relevant elements have been seen, and `forth` (with precondition `not` `after`) to advance the cursor to the next element. Procedure `forth` itself appears as forth -- Advance cursor by one position require not_after: not after deferred ensure moved_right: index = old index + 1 end where `index` -- another deferred feature -- is the integer position of the cursor. Although the above version of feature `count` is time-consuming -- it implies a whole traversal just for the purpose of determining the number of elements -- it has the advantage of being applicable to all variants, without any commitment to a choice of implementation, as would follow for example if we decided to treat `count` as an attribute. Proper descendants can always redefine `count` for more efficiency. Function `count` illustrates one of the most important contributions of the method to reusability: the ability to define '''behavior classes''' that capture common behaviors (such as count) while leaving the details of the behaviors (such as `start`, `after`, `forth`) open to many variants. As noted earlier, traditional approaches to reusability provide closed reusable components. A component such as `LIST`, although equipped with directly usable behaviors such as count, is open to many variations, to be provided by proper descendants. {{note|Some O-O languages support only the two extremes: fully effective classes, and fully deferred "interfaces", but not classes with a mix of effective and deferred features. This is an unacceptable limitation, negating the object-oriented method's support for a seamless, continuous spectrum from the most abstract to the most concrete. }} A class `B` inheriting from a deferred class `A` may provide implementations -- effective declarations -- for the features inherited in deferred form. In this case there is no need for a `redefine` subclause; the effective versions simply replace the inherited versions. The class is said to '''effect''' the corresponding features. If after this process there remain any deferred features, B is still considered deferred, even if it introduces no deferred features of its own, and must be declared as `class deferred`. In the example, classes such as `LINKED_LIST` and `ARRAYED_LIST` will effect all the deferred features they inherit from `LIST` -- `extend`, `start` etc. -- and hence will be effective. Except in some applications restricted to pure system modeling -- as discussed next -- the main benefit of deferred classes and features comes from polymorphism and dynamic binding. Because `extend` has no implementation in class `LIST`, a call of the form `my_list.extend(...)` with my_list of type `LIST [T]` for some `T` can only be executed if `my_list` is attached to a direct instance of an effective proper descendant of `LIST`, such as `LINKED_LIST`; then it will use the corresponding version of `extend`. Static binding would not even make sense here. Even an effective feature of `LIST` such as count may depend on deferred features (start and so on), so that a call of the form `my_list.count` can only be executed in the context of an effective descendant. All this indicates that a deferred class must have '''no direct instance'''. (It will have instances, the direct instances of its effective descendants.) If it had any, we could call deferred features on them, leading to execution-time impossibility. The rule that achieves this goal is simple: if the base type of `x` is a deferred class, no creation instruction of target `x`, of the form `create x...`, is permitted. ==Applications of deferred classes== Deferred classes cover abstract notions with many possible variants. They are widely used in Eiffel where they cover various needs:
* Capturing high-level classes, with common behaviors. * Defining the higher levels of a general taxonomy, especially in the inheritance structure of a library. * Defining the components of an architecture during system design, without commitment to a final implementation. * Describing domain-specific concepts in analysis and modeling. These applications make deferred classes a central tool of the Eiffel method's support for seamlessness and reversibility. The last one in particular uses deferred classes and features to model objects from an application domain, without any commitment to implementation, design, or even software (and computers). Deferred classes are the ideal tool here: they express the properties of the domain's abstractions, without any temptation of implementation bias, yet with the precision afforded by type declarations, inheritance structures (to record classifications of the domain concepts), and contracts to express the abstract properties of the objects being described. Rather than using a separate method and notation for analysis and design, this approach integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): it suffices to refine the deferred classes progressively by introducing effective elements, either by modifying the classes themselves, or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, will remain important; as we have seen ( [[ET: The Software Process in Eiffel#Seamlessness_and_reversibility|"Seamlessness and reversibility"]] ) this direct mapping property is a great help for extendibility. The following sketch (from the book [[Object-Oriented Software Construction, 2nd Edition]] ) illustrates these ideas on the example of scheduling the programs of a TV station. This is pure modeling of an application domain; no computers or software are involved yet. The class describes the notion of program segment. Note the use of assertions to define semantic properties of the class, its instances and its features. Although often presented as high-level, most object-oriented analysis methods (with the exception of Walden's and Nerson's Business Object Notation) have no support for the expression of such properties, limiting themselves instead to the description of broad structural relationships. note description: "Individual fragments of a broadcasting schedule" deferred class SEGMENT feature -- Access schedule: SCHEDULE deferred end -- Schedule to which segment belongs index: INTEGER deferred end -- Position of segment in its schedule starting_time, ending_time: INTEGER deferred end -- Beginning and end of scheduled air time next: SEGMENT deferred end -- Segment to be played next, if any sponsor: COMPANY deferred end -- Segment's principal sponsor rating: INTEGER deferred end -- Segment's rating (for children's viewing etc.) Minimum_duration: INTEGER = 30 -- Minimum length of segments, in seconds Maximum_interval: INTEGER = 2 -- Maximum time (seconds) between successive segments feature -- Element change set_sponsor (s: SPONSOR) require not_void: s /= Void deferred ensure sponsor_set: sponsor = s end ... change_next, set_rating omitted ... invariant in_list: (1 <= index) and (index <= schedule.segments.count) in_schedule: schedule.segments.item (index) = Current next_in_list: (next /= Void) implies (schedule.segments.item (index + 1) = next) no_next_if_last: (next = Void) = (index = schedule.segments.count) non_negative_rating: rating >= 0 positive times: (starting_time > 0) and (ending_time > 0) sufficient_duration: ending_time - starting_time >= Minimum_duration decent_interval: (next.starting_time) - ending_time <= Maximum_interval end ==Structural property classes== Some deferred classes describe a structural property, useful to the description of many other classes. Typical examples are classes of the Kernel Library in EiffelBase: `NUMERIC` describes objects on which arithmetic operations `+, -, *, /` are available, with the properties of a ring (associativity, distributivity, zero elements etc.). Kernel Library classes such as `INTEGER` and `REAL` -- but not, for example, `STRING` -- are descendants of `NUMERIC`. An application that defines a class `MATRIX` may also make it a descendant of `NUMERIC`. `COMPARABLE` describes objects on which comparison operations <, <=, >, >= are available, with the properties of a total preorder (transitivity, irreflexivity). Kernel Library classes such as `CHARACTER`, `STRING` and `INTEGER` -- but not our `MATRIX` example -- are descendants of `COMPARABLE`. For such classes it is again essential to permit effective features in a deferred class, and to include assertions. For example class `COMPARABLE` declares infix "<" as deferred, and expresses `>, >=` and <= effectively in terms of it. {{note|The type `like Current` will be explained in [[ET: Inheritance#Covariance, anchored declarations, and "catcalls"|"Covariance, anchored declarations, and "catcalls""]] ; you may understand it, in the following class, as equivalent to `COMPARABLE`. }} note description: "Objects that can be compared according to a total preorder relation" deferred class COMPARABLE feature -- Comparison infix "<" (other: like Current): BOOLEAN -- Is current object less than `other'? require other_exists: other /= Void deferred ensure asymmetric: Result implies not (other < Current) end infix "<=" (other: like Current): BOOLEAN -- Is current object less than or equal to `other'? require other_exists: other /= Void do Result := (Current < other) or is_equal (other) ensure definition: Result = (Current < other) or is_equal (other) end ... Other features: infix ">", min, max, ... invariant irreflexive: not (Current < Current) end -- class COMPARABLE ==Multiple inheritance and renaming== It is often necessary to define a new class in terms of several existing ones. For example: The Kernel Library classes `INTEGER` and `REAL` must inherit from both `NUMERIC` and `COMPARABLE`. A class `TENNIS_PLAYER`, in a system for keeping track of player ranking, will inherit from `COMPARABLE`, as well as from other domain-specific classes. A class `COMPANY_PLANE` may inherit from both `PLANE` and `ASSET`. Class `ARRAYED_LIST`, describing an implementation of lists through arrays, may inherit from both `LIST` and `ARRAY`. In all such cases multiple inheritance provides the answer. Multiple inheritance can cause '''name clashes''' : two parents may include a feature with the same name. This would conflict with the ban on name overloading within a class -- the rule that no two features of a class may have the same name. Eiffel provides a simple way to remove the name clash at the point of inheritance through the `rename` subclause, as in note description: "Sequential finite lists implemented as arrays" class ARRAYED_LIST [G] inherit LIST [G] ARRAY [G] rename count as capacity, item as array_item end feature ... end -- class ARRAYED_LIST Here both `LIST` and `ARRAY` have features called `count` and `item`. To make the new class valid, we give new names to the features inherited from `ARRAY`, which will be known within `ARRAYED_LIST` as `capacity` and `array_item`. Of course we could have renamed the `LIST` versions instead, or renamed along both inheritance branches. Every feature of a class has a '''final name''' : for a feature introduced in the class itself ("immediate" feature) it is the name appearing in the declaration; for an inherited feature that is not renamed, it is the feature's name in the parent; for a renamed feature, it is the name resulting from the renaming. This definition yields a precise statement of the rule against in-class overloading: {{rule|name=Final Name|text=Two different features of a class may not have the same final name. }} It is interesting to compare renaming and redefinition. The principal distinction is between features and feature names. Renaming keeps a feature, but changes its name. Redefinition keeps the name, but changes the feature. In some cases, it is of course appropriate to do both. Renaming is interesting even in the absence of name clashes. A class may inherit from a parent a feature which it finds useful for its purposes, but whose name, appropriate for the context of the parent, is not consistent with the context of the heir. This is the case with `ARRAY`'s feature `count` in the last example: the feature that defines the number of items in an array -- the total number of available entries -- becomes, for an arrayed list, the maximum number of list items; the truly interesting indication of the number of items is the count of how many items have been inserted in the list, as given by feature `count` from `LIST`. But even if we did not have a name clash because of the two inherited `count` features we should rename `ARRAY` 's `count` as `capacity` to maintain the consistency of the local feature terminology. The `rename` subclause appears before all the other feature adaptation subclauses -- `redefine` already seen, and the remaining ones `export`, `undefine` and `select` -- since an inherited feature that has been renamed sheds its earlier identity once and for all: within the class, and to its own clients and descendants, it will be known solely through the new name. The original name has simply disappeared from the name space. This is essential to the view of classes presented earlier: self-contained, consistent abstractions prepared carefully for the greatest enjoyment of clients and descendants. ==Inheritance and contracts== A proper understanding of inheritance requires looking at the mechanism in the framework of Design by Contract, where it will appear as a form of subcontracting. The first rule is that invariants accumulate down an inheritance structure: {{rule|name=Invariant Accumulation|text=The invariants of all the parents of a class apply to the class itself. }} The invariant of a class is automatically considered to include -- in the sense of logical "and" -- the invariants of all its parents. This is a consequence of the view of inheritance as an "is" relation: if we may consider every instance of `B` as an instance of `A`, then every consistency constraint on instances of `A` must also apply to instances of `B`. Next we consider routine preconditions and postconditions. The rule here will follow from an examination of what contracts mean in the presence of polymorphism and dynamic binding. Consider a parent `A` and a proper descendant `B` (a direct heir on the following figure), which redefines a routine `r` inherited from `A`. [[Image:tutorial-12]] As a result of dynamic binding, a call `a1`.`r` from a client `C` may be serviced not by `A`'s version of `r` but by `B` 's version if `a1`, although declared of type `A`, becomes at run time attached to an instance of `B`. This shows the combination of inheritance, redefinition, polymorphism and dynamic binding as providing a form of subcontracting; `A` subcontracts certain calls to `B`. The problem is to keep subcontractors honest. Assuming preconditions and postconditions as shown on the last figure, a call in `C` of the form if a1.pre then a1.r end or possibly a1.q a1.r where the postcondition of some routine `q` implies the precondition `pre` of `r`, satisfies the terms of the contract and hence is entitled to being handled correctly -- to terminate in a state satisfying `a1`.`post`. But if we let the subcontractor `B` redefine the assertions to arbitrary ''pre' ''and ''post','' this is not necessarily the case: ''pre' ''could be stronger than ''pre'', enabling `B` not to process correctly certain calls that are correct from `A`'s perspective; and ''post' ''could be weaker than ''post'', enabling `B` to do less of a job than advertized for `r` in the Contract Form of `A`, the only official reference for authors of client classes such as `C`. (An assertion `p` is stronger than or equal to an assertion `q` if `p` implies `q` in the sense of boolean implication.) The rule, then, is that for the redefinition to be correct the new precondition ''pre' ''must be weaker than or equal to the original ''pre'', and the new postcondition ''post' ''must be stronger than or equal to the original ''post''. Because it is impossible to check simply that an assertion is weaker or stronger than another, the language rule relies on different forms of the assertion constructs, `require else` and `ensure then`, for redeclared routines. They rely on the mathematical property that for any assertions `p` and `q`, the following are true: 1) p implies (p or q) 2) (p and q) implies p For a precondition, using `require else` with a new assertion will perform an `or`, which can only weaken the original; for a postcondition, `ensure then` will perform an `and`, which can only strengthen the original. Hence the rule: {{rule|name=Assertion Redeclaration|text=In the redeclared version of a routine, it is not permitted to use a require or ensure clause. Instead you may: Introduce a new condition with require else, for or-ing with the original precondition. Introduce a new condition with ensure then, for and-ing with the original postcondition. In the absence of such a clause, the original assertions are retained. }} The last case -- retaining the original -- is frequent but by no means universal. The Assertion Redeclaration rule applies to '''redeclarations'''. This terms covers not just redefinition but also effecting (the implementation, by a class, of a feature that it inherits deferred). The rules -- not just for assertions but also, as reviewed below, for typing -- are indeed the same in both cases. Without the Assertion Redeclaration rule, assertions on deferred features, such as those on `extend`, `count` and `forth` in [[ET: Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] , would be almost useless -- wishful thinking; the rule makes them binding on all effectings in descendants. From the Assertion Redeclaration rule follows an interesting technique: '''abstract preconditions'''. What needs to be weakened for a precondition (or strengthened for a postcondition) is not the assertion's concrete semantics but its abstract specification as seen by the client. A descendant can change the implementation of that specification as it pleases, even to the effect of strengthening the concrete precondition, as long as the abstract form is kept or weakened. The precondition of procedure `extend` in the deferred class `LIST` provided an example. We wrote the routine (in [[ET: Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] ) as extend (x: G) -- Add `x' at end of list. require space_available: not full deferred ensure one_more: count = old count + 1 end The precondition expresses that it is only possible to add an item to a list if the representation is not full. We may well consider -- in line with the Eiffel principle that whenever possible structures should be of unbounded capacity -- that `LIST` should by default make `full` always return false: full: BOOLEAN -- Is representation full? -- (Default: no) do Result := False end Now a class `BOUNDED_LIST` that implements bounded-size lists (inheriting, like the earlier `ARRAYED_LIST`, from both `LIST` and `ARRAY`) may redefine `full`: full: BOOLEAN -- Is representation full? -- (Answer: if and only if number of items is capacity) do Result := (count = capacity) end Procedure `extend` remains applicable as before; any client that used it properly with `LIST` can rely polymorphically on the `FIXED_LIST` implementation. The abstract precondition of `extend` has not changed, even though the concrete implementation of that precondition has in fact been strengthened. Note that a class such as `BOUNDED_LIST`, the likes of which indeed appear in EiffelBase, is not a violation of the Eiffel advice to stay away from fixed-size structures. The corresponding structures are bounded, but the bounds are changeable. Although `extend` requires `not full`, another feature, called `force` in all applicable classes, will add an element at the appropriate position by resizing and reallocating the structure if necessary. Even arrays in Eiffel are not fixed-size, and have a procedure `force` with no precondition, accepting any index position. The Assertion Redeclaration rule, together with the Invariant Accumulation rule, provides the right methodological perspective for understanding inheritance and the associated mechanisms. Defining a class as inheriting from another is a strong commitment; it means inheriting not only the features but the logical constraints. Redeclaring a routine is bound by a similar committment: to provide a new implementation (or, for an effecting, a first implementation) of a previously defined semantics, as expressed by the original contract. Usually you have a wide margin for choosing your implementation, since the contract only defines a range of possible behaviors (rather than just one behavior), but you '''must''' remain within that range. Otherwise you would be perverting the goals of redeclaration, using this mechanism as a sort of late-stage hacking to override bugs in ancestor classes. ==Join and uneffecting== It is not an error to inherit two deferred features from different parents under the same name, provided they have the same signature (number and types of arguments and result). In that case a process of '''feature join''' takes place: the features are merged into just one -- with their preconditions and postconditions, if any, respectively or-ed and and-ed. More generally, it is permitted to have any number of deferred features and at most one effective feature that share the same name: the effective version, if present will effect all the others. All this is not a violation of the Final Name rule (defined in [[ET: Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] ), since the name clashes prohibited by the rule involve two different features having the same final name; here the result is just one feature, resulting from the join of all the inherited versions. Sometimes we may want to join ''effective'' features inherited from different parents, assuming again the features have compatible signatures. One way is to redefine them all into a new version. That is, list each in a `redefine` clause, then write a redefined version of the feature. In this case, they again become one feature, with no name clash in the sense of the Final Name rule. But in other cases we may simply want one of the inherited implementations to take over the others. The solution is to revert to the preceding case by '''uneffecting''' the other features; uneffecting an inherited effective feature makes it deferred (this is the reverse of effecting, which turns an inherited deferred feature into an effective one). The syntax uses the `undefine` subclause: class D inherit A rename g as f -- g was effective in A undefine f end B undefine f -- f was effective in B end C -- C also has an effective feature f , which will serve as -- implementation for the result of the join. feature ... Again what counts, to determine if there is an invalid name clash, is the final name of the features. In this example, two of the joined features were originally called `f`; the one from `A` was called `g`, but in `D` it is renamed as `f`, so without the undefinition it would cause an invalid name clash. Feature joining is the most common application of uneffecting. In some non-joining cases, however, it may be useful to forget the original implementation of a feature and let it start a new life devoid of any burden from the past. ==Changing the export status== Another Feature Adaptation subclause, `export`, makes it possible to change the export status of an inherited feature. By default -- covering the behavior desired in the vast majority of practical cases -- an inherited feature keeps its original export status (exported, secret, selectively exported). In some cases, however, this is not appropriate: A feature may have played a purely implementation-oriented role in the parent, but become interesting to clients of the heir. Its status will change from secret to exported. In implementation inheritance (for example `ARRAYED_LIST` inheriting from `ARRAY`) an exported feature of the parent may not be suitable for direct use by clients of the heir. The change of status in this case is from exported to secret. You can achieve either of these goals by writing class D inherit A export {X, Y, ...} feature1, feature2, ... end ... This gives a new export status to the features listed (under their final names since, as noted, `export` like all other subclauses comes after `rename` if present): they become exported to the classes listed. In most cases this list of classes, `X`, `Y`, ..., consists of just `ANY`, to re-export a previously secret feature, or `NONE`, to hide a previously exported feature. It is also possible, in lieu of the feature list, to use the keyword `all` to apply the new status to all features inherited from the listed parent. Then there can be more than one class-feature list, as in class ARRAYED_LIST [G] inherit ARRAY [G] rename count as capacity, item as array_item, put as array_put export {NONE} all {ANY} capacity end ... where any explicit listing of a feature, such as `capacity`, takes precedence over the export status specified for `all`. Here most features of `ARRAY` are secret in `ARRAYED_LIST`, because the clients should not permitted to manipulate array entries directly: they will manipulate them indirectly through list features such as `extend` and `item`, whose implementation relies on `array_item` and `array_put`. But `ARRAY`'s feature `count` remains useful, under the name `capacity`, to the clients of `ARRAYED_LIST`. ==Flat and Flat-Contract Forms== Thanks to inheritance, a concise class text may achieve a lot, relying on all the features inherited from direct and indirect ancestors. This is part of the power of the object-oriented form of reuse, but can create a comprehension and documentation problem when the inheritance structures become deep: how does one understand such a class, either as client author or as maintainer? For clients, the Contract Form, entirely deduced from the class text, does not tell the full story about available features; and maintainers must look to proper ancestors for much of the relevant information. These observations suggest ways to produce, from a class text, a version that is equivalent feature-wise and assertion-wise, but has no inheritance dependency. This is called the '''Flat Form''' of the class. It is a class text that has no inheritance clause and includes all the features of the class, immediate (declared in the class itself) as well as inherited. For the inherited features, the flat form must of course take account of all the feature adaptation mechanisms: renaming (each feature must appear under its final name), redefinition, effecting, uneffecting and export status change. For redeclared features, `require else` clauses are or-ed with the precursors' preconditions, and `ensure then` clauses are and-ed with precursors' postconditions. For invariants, all the ancestors' clauses are concatenated. As a result, the flat form yields a view of the class, its features and its assertions that conforms exactly to the view offered to clients and (except for polymorphic uses) heirs. As with the Contract Form ( [[ET: Design by Contract (tm), Assertions and Exceptions#The_contract_form_of_a_class|"The contract form of a class"]] ), producing the Flat Form is the responsibility of tools in the development environment. In EiffelStudio, you will just click the "Flat" icon. The Contract Form of the Flat Form of a class is known as its '''Flat-Contract Form'''. It gives the complete interface specification, documenting all exported features and assertions -- immediate or inherited -- and hiding implementation aspects. It is the appropriate documentation for a class. ==Repeated inheritance and selection== An inheritance mechanism, following from multiple inheritance, remains to be seen. Through multiple inheritance, a class can be a proper descendant of another through more than one path. This is called repeated inheritance and can be indirect, as in the following figure, or even direct, when a class `D` lists a class `A` twice in its `inherit` clause. [[Image:tutorial-13]] The figure's particular example is in fact often used by introductory presentations of multiple inheritance, which is a pedagogical mistake: simple multiple inheritance examples (such as `INTEGER` inheriting from `NUMERIC` and `COMPARABLE`, or `COMPANY_PLANE` from `ASSET` and `PLANE`) should involve the combination of '''separate abstractions'''. Repeated inheritance is an advanced technique; although invaluable, it does not arise in elementary uses and requires a little more care. In fact there is only one non-trivial issue in repeated inheritance: what does a feature of the repeated ancestor, such as `change_address` and `computer_account`, mean for the repeated descendant, here `TEACHING_ASSISTANT`? (The example features chosen involve a routine and an attribute; the basic rules will be the same.) There are two possibilities: sharing (the repeatedly inherited feature yields just one feature in the repeated descendant) and duplication (it yields two). Examination of various cases shows quickly that a fixed policy, or one that would apply to all the features of a class, would be inappropriate. Feature `change_address` calls for sharing: as a teaching assistant, you may be both teacher and student, but you are just one person, with just one official domicile. If there are separate accounts for students' course work and for faculty, you may need one of each kind, suggesting that `computer_account` calls for duplication. The Eiffel rule enables, once again, the software developer to craft the resulting class so as to tune it to the exact requirements. Not surprisingly, it is based on names, in accordance with the Final Name rule (no in-class overloading): {{rule|name=Repeated Inheritance|text=
A feature inherited multiply under one name will be shared: it is considered to be just one feature in the repeated descendant.
A feature inherited multiply under different names will be replicated, yielding as many variants as names. }} So to tune the repeated descendant, feature by feature, for sharing and replication it suffices to use renaming. Doing nothing will cause sharing, which is indeed the desired policy in most cases (especially those cases of unintended repeated inheritance: making `D` inherit from `A` even though it also inherits from `B`, which you forgot is already a descendant of `A`). If you use renaming somewhere along the way, so that the final names are different, you will obtain two separate features. It does not matter where the renaming occurs; all that counts is whether in the common descendant, `TEACHING_ASSISTANT` in the last figure, the names are the same or different. So you can use renaming at that last stage to cause replication; but if the features have been renamed higher you can also use last-minute renaming to avoid replication, by bringing them back to a single name. The Repeated Inheritance rule gives the desired flexibility to disambiguate the meaning of repeatedly inherited features. There remains a problem in case of redeclaration and polymorphism. Assume that somewhere along the inheritance paths one or both of two replicated versions of a feature `f`, such as `computer_account` in the example, has been redeclared; we need to define the effect of a call `a.f` ( `a.computer_account` in the example) if `a` is of the repeated ancestor type, here `UNIVERSITY_PERSON`, and has become attached as a result of polymorphism to an instance of the repeated descendant, here `TEACHING_ASSISTANT`. If one or more of the intermediate ancestors has redefined its version of the feature, the dynamically-bound call has two or moreversions to choose from. A `select` clause will resolve the ambiguity, as in class TEACHING_ASSISTANT inherit TEACHER rename computer_account as faculty_account select faculty_account end STUDENT rename computer_account as student_account end ... We assume here that that no other renaming has occurred -- `TEACHING_ASSISTANT` takes care of the renaming to ensure replication -- but that one of the two parents has redefined `computer_account`, for example `TEACHER` to express the special privileges of faculty accounts. In such a case the rule is that one (and exactly one) of the two parent clauses in `TEACHING_ASSISTANT` '''must''' select the corresponding version. Note that no problem arises for an entity declared as ta: TEACHING_ASSISTANT since the valid calls are of the form `ta.faculty_account` and `ta.student_account`, neither of them ambiguous; the call `ta.computer_account` would be invalid, since after the renamings class `TEACHING_ASSISTANT` has no feature of that name. The `select` only applies to a call up.computer_account with `up` of type `UNIVERSITY_PERSON`, dynamically attached to an instance of `TEACHING_ASSISTANT`; then the `select` resolves the ambiguity by causing the call to use the version from `TEACHER`. So if you traverse a list `computer_users: LIST [UNIVERSITY_PERSON]` to print some information about the computer account of each list element, the account used for a teaching assistant is the faculty account, not the student account. You may, if desired, redefine `faculty_account` in class `TEACHING_ASSISTANT`, using `student_account` if necessary, to take into consideration the existence of another account. But in all cases we need a precise disambiguation of what `computer_account` means for a `TEACHING_ASSISTANT` object known only through a `UNIVERSITY_PERSON` entity. The `select` is only needed in case of replication. If the Repeated Inheritance rule would imply sharing, as with change_address, and one or both of the shared versions has been redeclared, the Final Name rule makes the class invalid, since it now has '''two different features''' with the same name. (This is only a problem if both versions are effective; if one or both are deferred there is no conflict but a mere case of feature joining as explained in [[ET: Inheritance#Join_and_uneffecting|"Join and uneffecting"]] .) The two possible solutions follow from the previous discussions: If you do want sharing, one of the two versions must take precedence over the other. It suffices to '''undefine''' the other, and everything gets back to order. Alternatively, you can redefine both into a new version, which takes precedence over both. If you want to keep both versions, switch from sharing to replication: rename one or both of the features so that they will have different names; then you must `select` one of them. ==Constrained genericity== Eiffel's inheritance mechanism has an important application to extending the flexibility of the '''genericity''' mechanism. In a class `SOME_CONTAINER [G]`, as noted in [[ET: Genericity and Arrays|"Genericity and Arrays"]] ), the only operations available on entities of type `G`, the formal generic parameter, are those applicable to entities of all types. A generic class may, however, need to assume more about the generic parameter, as with a class `SORTABLE_ARRAY [G ...]` which will have a procedure `sort` that needs, at some stage, to perform tests of the form if item (i) < item (j) then ... where `item (i)` and `item (j)` are of type `G`. But this requires the availability of a feature infix "<" in all types that may serve as actual generic parameters corresponding to `G`. Using the type `SORTABLE_ARRAY [INTEGER]` should be permitted, because `INTEGER` has such a feature; but not `SORTABLE_ARRAY [COMPLEX]` if there is no total order relation on `COMPLEX`. To cover such cases, declare the class as class SORTABLE_ARRAY [G -> COMPARABLE] making it '''constrained generic'''. The symbol `->` recalls the arrow of inheritance diagrams; what follows it is a type, known as the generic constraint. Such a declaration means that: Within the class, you may apply the features of the generic constraint -- here the features of `COMPARABLE`: infix "<", `infix ">"` etc. -- to expressions of type `G`. A generic derivation is only valid if the chosen actual generic parameter conforms to the constraint. Here you can use `SORTABLE_ARRAY [INTEGER]` since `INTEGER` inherits from `COMPARABLE`, but not `SORTABLE_ARRAY [COMPLEX]` if `COMPLEX` is not a descendant of `COMPARABLE`. A class can have a mix of constrained and unconstrained generic parameters, as in the EiffelBase class `HASH_TABLE [G, H -> HASHABLE]` whose first parameter represents the types of objects stored in a hash table, the second representing the types of the keys used to store them, which must be `HASHABLE`. As these examples suggest, structural property classes such as `COMPARABLE`, `NUMERIC` and `HASHABLE` are the most common choice for generic constraints. Unconstrained genericity, as in `C [G]`, is defined as equivalent to `C [G -> ANY]`. ==Assignment attempt== {{Caution|As of version 7.1, the assignment attempt has been marked as obsolete. Use the object test (described [[ET: Inheritance#Object test|below]] in a variant of this same discussion) instead. The documentation for the assignment attempt will remain during a period of transition, but will be removed at some point in the future. }} The Type Conformance rule ( [[ET: Inheritance#Polymorphism|"Polymorphism"]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target. Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[ET: The Dynamic Structure: Execution Model#Deep_operations_and_persistence|"Deep operations and persistence"]] ) includes, along with the procedure `store` seen there, the reverse operation, a function `retrieved` which yields an object structure retrieved from a file or network, to which it was sent using `store`. But `retrieved` as declared in the corresponding class `STORABLE` of EiffelBase can only return the most general type, `ANY`; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent. In such cases you cannot trust the declared type but must check it against the type of an actual run-time object. Eiffel introduces for this purpose the '''assignment attempt''' operation, written x ?= y with the following effect (only applicable if `x` is a writable entity of reference type): If `y` is attached, at the time of the instruction's execution to an object whose type conforms to the type of `x`, perform a normal reference assignment. Otherwise (if `y` is void, or attached to a non-conforming object), make `x` void. Using this mechanism, a typical object structure retrieval will be of the form x ?= retrieved if x = Void then "We did not get what we expected" else "Proceed with normal computation, which will typically involve calls of the form x.some_feature " end As another application, assume we have a `LIST [ACCOUNT]` and class `SAVINGS_ACCOUNT`, a descendant of `ACCOUNT`, has a feature `interest_rate` which was not in `ACCOUNT`. We want to find the maximum interest rate for savings accounts in the list. Assignment attempt easily solves the problem: local s: SAVINGS_ACCOUNT do from account_list.start until account_list.after loop s ?= acc_list.item -- item from LIST yields the element at -- cursor position if s /= Void and then s.interest_rate > Result then -- Using and then (rather than and) guarantees -- that s.interest_rate is not evaluated -- if s = Void is true. Result := s.interest_rate end account_list.forth end end Note that if there is no savings account at all in the list the assignment attempt will always yield void, so that the result of the function will be 0, the default initialization. Assignment attempt is useful in the cases cited -- access to external objects beyond the software's own control, and access to specific properties in a polymorphic data structure. The form of the instruction precisely serves these purposes; not being a general type comparison, but only a verification of a specific expected type, it does not carry the risk of encouraging developers to revert to multi-branch instruction structures, for which Eiffel provides the far preferable alternative of polymorphic, dynamically-bound feature calls. ==Object test== The Type Conformance rule ( [[ET: Inheritance#Polymorphism|"Polymorphism"]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target. Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[ET: The Dynamic Structure: Execution Model#Deep_operations_and_persistence|"Deep operations and persistence"]] ) includes, along with the procedure `store` seen there, the reverse operation, a function `retrieved` which yields an object structure retrieved from a file or network, to which it was sent using `store`. But `retrieved` as declared in the corresponding class `STORABLE` of EiffelBase can only return the most general type, `ANY`; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent. In such cases you cannot trust the declared type but must check it against the type of an actual run-time object. Eiffel introduces for this purpose the '''object test''' operation, using a form of the [[Void-safety: Background, definition, and tools#The attached syntax (object test)|attached syntax]]. The complete attached syntax is: attached {SOME_TYPE} exp as l_exp and is a boolean-valued expression. So we can use the attached syntax as an object test. A typical object structure retrieval will be of the form if attached retrieved as l_temp then -- We got what we expected -- Proceed with normal computation, typically involving calls of the form l_temp.some_feature else -- We did not get what we expected" end The expression `attached retrieved as l_temp` tests `retrieved` for voidness. If `retrieved` is not void, that is, `retrieved` is currently attached to an object, then a fresh local entity `l_temp` is created, the object is attached to `l_temp`, and the value of the expression is `True`. If `retrieved` is void, then the value of the expression is `False`. As another application, assume we have a `LIST [ACCOUNT]` and class `SAVINGS_ACCOUNT`, a descendant of `ACCOUNT`, has a feature `interest_rate` which was not in `ACCOUNT`. We want to find the maximum interest rate for savings accounts in the list. Object test easily solves the problem: do from account_list.start until account_list.after loop if attached {SAVINGS_ACCOUNT} account_list.item as l_s and then l_s.interest_rate > Result then -- Using and then (rather than and) guarantees -- that l_s.interest_rate is not evaluated -- if `attached {SAVINGS_ACCOUNT} account_list.item as l_s' is False. Result := l_s.interest_rate end account_list.forth end end Note that if there is no savings account at all in the list the object test will never be satisfied, so that the result of the function will be 0, the default initialization. The object test is useful also in building [[Void-safe programming in Eiffel|void-safe software systems]]. {{SeeAlso|[[Creating a new void-safe project#More about the attached syntax|More about the attached syntax]] in the section on [[Void-safe programming in Eiffel]]. }} ==Covariance, anchored declarations, and "catcalls"== The final properties of Eiffel inheritance involve the rules for adapting not only the implementation of inherited features (through redeclaration of either kind, effecting and redefinition, as seen so far) and their contracts (through the Assertion Redeclaration rule), but also their types. More general than type is the notion of a feature's '''signature''', defined by the number of its arguments, their types, the indication of whether it has a result (that is to say, is a function or attribute rather than a procedure) and, if so, the type of the result. ===Covariance=== In many cases the signature of a redeclared feature remains the same as the original's. But in some cases you may want to adapt it to the new class. Assume for example that class `ACCOUNT` has features owner: HOLDER set_owner (h: HOLDER) -- Make `h' the account owner. require not_void: h /= Void do owner := h end We introduce an heir `BUSINESS_ACCOUNT` of `ACCOUNT` to represent special business accounts, corresponding to class `BUSINESS` inheriting from `HOLDER`: [[Image:tutorial-14]] Clearly, we must redefine `owner` in class `BUSINESS_ACCOUNT` to yield a result of type `BUSINESS`; the same signature redefinition must be applied to the argument of `set_owner`. This case is typical of the general scheme of signature redefinition: in a descendant, you may need to redefine both results and arguments to types conforming to the originals. This is reflected by a language rule: {{rule|name=Covariance|text=In a feature redeclaration, both the result type if the feature is a query (attribute or function) and the type of any argument if it is a routine (procedure or function) must conform to the original type as declared in the precursor version. }} The term "covariance" reflects the property that all types -- those of arguments and those of results -- vary together in the same direction as the inheritance structure. If a feature such as `set_owner` has to be redefined for more than its signature -- to update its implementation or assertions -- the signature redefinition will be explicit. For example `set_owner` could do more for business owners than it does for ordinary owners. Then the redefinition will be of the form set_owner (b: BUSINESS) -- Make b the account owner. do ... New routine body ... end ====Anchored Declarations==== In other cases, however, the body will be exactly the same as in the precursor. Then explicit redefinition would be tedious, implying much text duplication. The mechanism of '''anchored redeclaration''' solves this problem. The original declaration of `set_owner` in `ACCOUNT` should be of the form set_owner (h: like owner) -- Make h the account owner. -- The rest as before: require not_void: h /= Void do owner := h end A `like` ''anchor'' type, known as an anchored type, may appear in any context in which ''anchor'' has a well-defined type; that is, ''anchor'' can be an attribute or function of the enclosing class. Then, assuming `T` is the type of ''anchor'', the type `like` ''anchor'' means the following: In the class in which it appears, `like` ''anchor'' means the same as `T`. For example, in `set_owner` above, the declaration of `h` has the same effect as if `h` had been declared of type `HOLDER`, the type of the anchor `owner` in class `ACCOUNT`. The difference comes in proper descendants: if a type redefinition changes the type of ''anchor'', any entity declared `like` ''anchor'' will be considered to have been redefined too. This means that anchored declarations are a form of of implicit covariant redeclaration. In the example, class `BUSINESS_ACCOUNT` only needs to redefine the type of `owner` (to `BUSINESS`). It doesn't have to redefine `set_owner` except if it needs to change its implementation or assertions. It is possible to use `Current` as anchor; the declaration `like Current` denotes a type based on the current class (with the same generic parameters if any). This is in fact a common case; we saw in [[ET: Inheritance#Structural_property_classes|"Structural property classes"]] , that it applies in class `COMPARABLE` to features such as is_less alias "<" (other: like Current): BOOLEAN ... since we only want to compare two comparable elements of compatible types -- but not, for example, integer and strings, even if both types conform to `COMPARABLE`. (A "balancing rule" makes it possible, however, to mix the various arithmetic types, consistently with mathematical traditions, in arithmetic expressions such as `3 + 45.82` or boolean expressions such as 3 < 45.82.) Similarly, class `ANY` declares procedure `copy` as copy (other: like Current) ... with the argument anchored to the current object. A final, more application-oriented example of anchoring to `Current` is the feature `merge` posited in an earlier example (in [[ET: The Dynamic Structure: Execution Model|"The Dynamic Structure: Execution Model"]] ) with the signature `merge (other: ACCOUNT)`. By using instead `merge (other: like Current)` we can ensure that in any descendant class -- `BUSINESS_ACCOUNT`, `SAVINGS_ACCOUNT`, `MINOR_ACCOUNT` ... -- an account will only be mergeable with another of a compatible type. ====Qualified Anchored Declarations==== The anchored types shown above specify anchors which are either: * The name of a query of the class in which the anchored declaration appears ** as in the case of: `set_owner (h: like owner)` or * `Current` ** as in the case of: is_less alias "<" (other: like Current): BOOLEAN. Declarations can also use '''qualified''' anchored types. Consider this possible feature of `ACCOUNT`: owner_name: like owner.name Here the type of `owner_name` is determined as the type of the feature `name` as applied to the type of the feature `owner` of the current class. As you can imagine, for declarations like this to be valid, the feature names `name` and `owner` must be the names queries, i. e., the names of attributes or functions. This notion can be extended to declare the type through multiple levels of remoteness, so patterns like the following can be valid: f: like a.b.c.d For example if a class used a list of items of type `ACCOUNT`, it might include a declaration for that list: all_accounts: LINKED_LIST [ACCOUNT] -- All my accounts This class could declare a feature with a qualified anchored type like this: account_owner_name: like all_accounts.item.owner.name A qualified anchored type can be qualified also by specifying a type for the qualifier: owner_name: like {HOLDER}.name In this case, the type of `owner_name` is the same as the type of the `name` feature of type `HOLDER`. Anchored declarations serve as another way to make software more concise and more resilient in a changing world. Let's look at one last example of using a qualified anchored type: a: ARRAY [DATA] ... local idx: like a.lower do from idx := a.lower until idx > a.upper ... Declaring the local entity `idx` as the qualified anchored type `like a.lower` puts this code (well, actually the producer of this code) in the enviable position of never having to worry about what type is used by class `ARRAY` for its feature `lower`. So, `{ARRAY}.lower` could be implemented as `INTEGER_32`, `NATURAL_64`, or some other similar type and this code would be fine, even if at some point that type changed. ===Catcalls=== In our diversion about anchored declarations, we've gotten away from our discussion of covariance. Let's continue that now with a look at a side effect of covariance known as the '''catcall'''. Covariance makes static type checking more delicate; mechanisms of '''system validity''' and '''catcalls''' address the problem, discussed in detail in the book [[Object-Oriented Software Construction, 2nd Edition]]. The capabilities of polymorphism combined with covariance provide for powerful and flexible modeling. Under certain conditions, though, this flexibility can lead to problems. In short, you should be careful to avoid polymorphic '''catcalls'''. The '''call''' part of '''catcall''' means feature call. The '''cat''' part is an acronym for '''C'''hanged '''A'''vailability or '''T'''ype. What is changing here are features of descendant classes through the adaptation of inheritance. So maybe a descendant class has changed the export status of an inherited feature, so that that feature is not available on instances of the descendant class ... this is the case of '''changed availability'''. Or perhaps, through covariant modeling, the type of an argument to a feature in a descendant class has changed ... the case of '''changed type'''. Let's look at an example of changed type, due to covariant modeling. Suppose we have a system which uses the classes depicted on the following diagram: [[Image:Catcall example class diagram]] If in a client class, we declare the following attributes: my_animal: ANIMAL my_food_stuff: FOOD_STUFF Also, the class `ANIMAL` contains the feature: eat (a_f: FOOD_STUFF) -- Consume `a_f' deferred end This routine is implemented in `COW` as: eat (a_f: GRASS) and in class `LION` as: eat (a_f: WILDEBEEST_FILET) So, covariant modeling is used to make the type of the argument for `eat` appropriate for each of `ANIMAL`'s heirs. Here's where the problem comes in. It is possible at run-time to attach to `my_animal` a direct instance of either `COW` or `LION`. So, `my_animal` is a polymorphic attribute. Likewise, it is possible at run-time that `my_food_stuff` could be attached to a direct instance of either `GRASS` or `WILDEBEEST_FILET`. So, the feature call: my_animal.eat (my_food_stuff) is a '''catcall''', because there is a possibility that through the changing type of the argument to `eat`, we could be causing a `COW` to engage in the inappropriate practice of eating a `WILDEBEEST_FILET`. Because this possibility exists, developers should exercise caution in using polymorphism and covariant modeling. In version 6.2 of EiffelStudio, a capability was added to detect harmful catcalls at runtime. So, in our example, if we used `my_animal.eat (my_food_stuff)` only to feed grass to cows and wildebeest filets to lions, then all would be well. But if we attempted to use that same call to feed an inappropriate food to an animal, we would see an exception. Likewise the compiler in EiffelStudio will produce warnings in cases in which catcalls are possible. Below is an example of the compiler warning issued on the example. [[Image:Catcall compiler warning]] ==Non-conforming inheritance== So far, our experience with inheritance is that of "conforming" inheritance ... the most commonly used type of inheritance. Conforming inheritance is what allows a direct instance (in the '''catcall''' example above) of `COW` to be attached at runtime to an entity of type `ANIMAL`. This can be a powerful modeling capability, but it is this same polymorphism facilitated by conforming inheritance that puts us in the danger of using polymorphic '''catcalls'''. In cases in which polymorphic attachment is not anticipated, the possibility of catcalls can be avoided by using '''non-conforming inheritance'''. Non-conforming inheritance is just a more restrictive form of inheritance. :'''Non-conforming inheritance allows features to be inherited from parent to heir, but it disallows polymorphic attachment of a direct instance of an heir to an entity based on a non-conforming parent.''' In order to use non-conforming inheritance for a particular parent, we use the marker `{NONE}` in the appropriate inheritance part of the class: class MY_HEIR_CLASS inherit MY_CONFORMING_PARENT inherit {NONE} MY_NON_CONFORMING_PARENT ... Here there are two `inherit` clauses, one to specify conforming parents, and one to specify non-conforming parents. The clause specifying the conforming inheritance must precede the one specifying the non-conforming inheritance. {{note|According to the Eiffel programming language [http://www.ecma-international.org/publications/standards/Ecma-367.htm standard], it is possible to have any number of inherit clauses an any order, however EiffelStudio versions as late as 6.5 allow only one conforming and one non-conforming clause, with the conforming clause preceding the non-conforming one. This restriction will be removed in a future release.}} So, in this case, at runtime it is valid for a direct instance of `MY_HEIR_CLASS` to be attached to an entity of type `MY_CONFORMING_PARENT`, but not to an entity of type `MY_NON_CONFORMING_PARENT`. Accordingly, the compiler would reject any code in which an instance of `MY_HEIR_CLASS` could become attached to an entity of type `MY_NON_CONFORMING_PARENT`. Because the polymorphic attachment cannot be made, the possibility of a catcall is avoided. {{note|As implemented, non-conforming inheritance mimics a copy/paste operation in which the features of the parent class are copied to the non-conforming heir class with no inheritance linkage maintained. You should keep this fact in mind when using non-conforming inheritance. In particular, `once` routines are replicated as unrelated features in the heir classes, so they share neither freshness status nor computed value (in the case of functions). Thus, a once function that comes from a non-conforming parent yields a result that is not related to the one returned by the parent's version.}}