       Teil 3  GENERISCHE KLASSEN, TYPEN, VERERBUNG, KONFORMITT, DYNAMISCHE 
               BINDUNG, ANKERTYPEN
               =============================================================

       Klassen knnen formale Parameter haben.       
       Typische Beispiele fr solche  GENERISCHEN  Klassen sind die Standard-
       klassen
                                  STACK [G]  
                                  QUEUE [G]

       wo der formale Parameter  G  einen beliebigen Typ reprsentiert.
       [ Stack und Queue sind Beispiele fr Container-Klassen, die (wie etwa
       auch  ARRAY [G] ) dazu dienen, Objekte eines gewissen Typs zu lagern,
       um sie zu einem spteren Zeitpunkt wieder hervorzuholen. ]
       
       
       Eiffel ist eine getypte Sprache. 
       Jede Gre ist von einem gewissen Typ, und jeder Typ basiert auf einer 
       Klasse.
       Beispielsweise wird durch die Deklaration
                               
                                  p: POINT
       
       die Gre  p  vom Typ  POINT  deklariert.
       
       Klassen und Typen sind verwandte Begriffe. Der Unterschied zwischen 
       beiden ist fr nichtgenerische Klassen eher begrifflich als substan-
       tiell.
       Der Typ beschreibt die offizielle Schnittstelle der Klasse.
       Die Klasse (der Klassentext) ist die Implementation der Schnittstelle.
       Jedes bei Laufzeit entstehende Objekt ist eine Ausprgung, ein Daten- 
       satz des Typs.

       Klasse und Typ liegen weiter auseinander fr generische Klassen.
       Generische Klassen sind Typmuster; sie bentigen einen aktuellen Para-
       meter, um einen Typ zu definieren.
       Also:  ARRAY [G]  ist kein Typ, aber durch  a: ARRAY [INTEGER]  erhlt
       a  den Typ  ARRAY [INTEGER] .
       
       Der Typ einer Gre gibt an, welche features fr eine Gre verfgbar
       sind.
       Ist die Gre  a  vom Typ  C , so ist der Feature-Aufruf  a.f  korrekt,
       genau dann wenn  f  ein feature der Klasse  C  ist.
       Letzteres kann vom Compiler geprft werden. Also ist Typprfung eine 
       statische Angelegenheit; diesbezgliche Fehler zeigen sich beim Kompi-
       lieren und nicht erst bei Laufzeit.


       ---------------------------------------------------------------------

       
       Das Prinzip der statischen Typprfung wird in Eiffel flexibel gemacht
       durch die Mglichkeit der "Dynamischen Bindung" (wird weiter unten
       genauer erlutert).
       Die Grundlage dieser dynamischen Bindung sind Vererbung und Konformi-
       tt.

       Ein Beispiel fr Vererbung war die Klasse  TUPFER  aus Teil 1.
       Die Klasse  TUPFER  erbt die Punktkoordinaten von der Klasse  POINT  
       und fgt das tupfer-spezifische Farb-Attribut  c: COLOUR  hinzu.
       
       Sprechweise:  POINT  ist Vorfahr von  TUPFER , und  TUPFER  ist Nach-
       komme von  POINT .
                                   
                                   POINT
                                     |
                                   TUPFER

       
       Ein hnliches Beispiel wre die (Phantasie-) Klasse
                                 
                                 class LKW
       
       die zweckmigerweise von einer Klasse  
                                 
                                 class KFZ  
       
       erbt und die Lkw-spezifischen features wie
                              
                              nutzlast: INTEGER
       hinzufgt.

       
       In Eiffel kann Vererbung mehrfach sein. Dies bedeutet, da eine Klasse
       mehr als ein "Elternteil" haben kann.
       Ein (Phantasie-) Beispiel wre die Klasse  class FAHRLEHRER , die von
       den beiden Klassen  class AUTOFAHRER  und  class LEHRER  erbt und die
       fr Fahrlehrer spezifischen features hinzufgt:

                        AUTOFAHRER            LEHRER
                                 \           /
                                   FAHRLEHRER 

       
       
       Ein in Eiffel/S reales Beispiel ist die Standardklasse


       deferred class SORTED_COLLECTION [G -> COMPARABLE]

       inherit
           COLLECTION [G]
               undefine
                   iterator
           end
           
           TWOWAY_TRAVERSABLE
           end

       feature
           iter_after (x : G) : TWOWAY_ITER is
                 -- Returns an iterator object which is prepared to
                 -- traverse the current collection in increasing order
                 -- beginning at the first element >= `x'.
               do
                  ...
               end

           iter_before (x : G) : TWOWAY_ITER is
                 -- Returns an iterator object which is prepared to
                 -- traverse the current collection in decreasing order
                 -- beginning at the first element <= `x'.
               do
                  ...
               end

       feature { NONE }
       ...

       end -- class SORTED_COLLECTION


       Diese aufgeschobene generische Klasse  SORTED_COLLECTION  dient dazu,
       sortierte Mengen eines allgemeinen Typs  G  zu reprsentieren.
       SORTED_COLLECTION  erbt die mengenspezifischen features von von der
       Klasse  COLLECTION . 
       Auerdem erbt  SORTED_COLLECTION  von der Klasse TWOWAY_TRAVERSABLE
       die Maschinerie, um die sortierte Menge zu durchlaufen.
       
       COLLECTION  enthlt features wie
                 count    (Attribut; Zahl der Elemente)
                 empty    (Funktion; true, falls die Menge leer ist)
                 add      (Prozedur; fgt ein Element hinzu)
                 remove   (Prozedur; entfernt ein Element)

       Die Klasse  TWOWAY_TRAVERSABLE  enthlt die Funktion
                           iterator: TWOWAY_ITER
       Die Datenstruktur  iterator  liefert bei Laufzeit ein Objekt, das auf
       der sortierten Menge sozusagen "sitzt" und das mit den features 
                                    first
                                    last 
                                    forth 
                                    back
                                    stop
       ausgestattet ist, mit denen es sich auf "seiner" Menge bewegen kann.
       
       Zu allen Klassen des Container-Clusters gehren solche Iteratoren, die
       sich durch "ihren" Container bewegen knnen. (Weiteres dazu in Teil 4.)

       
       Die Klasse  SORTED_COLLECTION [G -> COMPARABLE]  ist ein Beispiel fr
       eine  EINGESCHRNKTE  generische Klasse. 
       Denn fr die in einen sortierten Container eingelagerten Objekte mu
       natrlich vorausgesetzt werden, da zwischen ihnen eine vollstndige
       Ordnung besteht.
       Die Klausel  G -> COMPARABLE  bedeutet, da der Typ von  G  nicht be-
       liebig sein darf, sondern zu  COMPARABLE  konform sein mu (zum Begriff
       der Konformitt siehe unten).

       
       Die Vererbungsstruktur eines Eiffelprogramms kann man sich als gerich-
       teten Graphen vorstellen:
                        
       Vererbungsgraph     AUTOFAHRER            LEHRER
       (Kantenrichtung             \           /
       von unten nach               FAHRLEHRER 
       oben)

       Dieser Graph darf keine gerichteten Kreise enthalten. Er mu azyklisch 
       sein, wenn die Vererbungsstruktur nicht widersprchlich sein soll. 
       (Im Gegensatz dazu man darf sich bei Kunden-Zugriff durchaus direkt
       oder indirekt auf die eigene Klasse beziehen.)
       Obgleich die Vererbungsstruktur azyklisch ist, kann eine Klasse durch-
       aus auf mehreren Wegen von einer anderen Klasse erben. In Eiffel ist  
       "wiederholtes"  Erben mglich:

                                  
                                    A      (A enthlt feature f)
                                 /     \
                               B1       B2
                                 \     /
                                    C

       
       Hierbei knne jedoch Probleme auftauchen.
       In der Situation der Skizze erbt die Klasse  C  das feature  f  zwei-
       mal, sowohl ber  B1  als auch ber  B2 .
       Normalerweise ist das kein Problem: Auch die Klasse  C  enthlt dann
       eben das feature f .
       Problematischer wird es, wenn in  B1  und/oder in  B2  das feature  f
       umdefiniert und/oder umbenannt wird (ein in Eiffelkklassen hufiges
       Vorgehen). In diesem Falle ist nicht unmittelbar klar, wie das feature
       f  fr die Klasse  C  zu verstehen sein soll, ob als das von  B1  oder
       das von  B2  geerbte.
       Die mit solchem wiederholten Erben verbundenen Mglichkeiten und Pro-
       bleme gehren zu den eher subtilen Aspekten der Programmiersprache
       Eiffel, nheres dazu siehe Handbuch Teil 11.


       -----------------------------------------------------------------------


       Konformitt betrifft die Typen.
       
       Sei                          x: T        eine Gre vom Typ  T , 
       und sei                      y: V        vom Typ  V .
       
       Dann ist der Typ  V  KONFORM  zum Typ  T , wenn die zu  V  gehrende
       Klasse ein Nachkomme der zu  T  gehrenden Klasse ist.

       Im Falle von generischen Typen wird entsprechend verlangt, da die 
       aktuellen generischen Parameter von  V  zu den entsprechenden Parame-
       tern von  T  konform sind.

       
       Konformitt regiert die Gltigkeit von Zuweisungen und berhaupt die
       Typenkorrektheit in verschiedenen Situationen.
       
       Sei wieder                   x: T           vom Typ  T  und  
                                    y: V           vom Typ  V .
       Dann mu  V  konform zu  T  sein fr die Korrektheit der folgenden
       Aktionen (vergl. Handbuch S.218):
       -  die Zuweisung  x := y
       -  der Feature-Aufruf  r(...,y,...) , wobei  y  das aktuelle Argument
          der Routine  r  ist, das an die Stelle des formalen Arguments  x    
          der Feature-Deklaration tritt
       -  jede Benutzung eines generischen Typs der Form  C[...,V,...]  mit 
          aktuellem Parameter  V , wobei der zugehrge formale Parameter
          durch  T  eingeschrnkt ist (d.h. die Klasse ist als 
          C[...,G->T,...]  deklariert)

       Ferner ist es bei Neudefinition einer Routine in einer Nachfolger-
       klasse legitim, den Typ eines formalen Arguments zu ersetzen durch
       einen konformen Typ. (Covariant argument typing policy. Vergl. Hand-
       buch S.358). - Diese Regel wird Probleme bei der Typkontrolle aufwer-
       fen, siehe unten.

       
       Ein Beispiel fr den dritten Fall ist die oben erwhnte Eifel-Klasse
       SORTED_COLLECTION [G -> COMPARABLE] , wo also fr den formalen Parame-
       ter  G  nur solche aktuellen Parameter eingesetzt werden drfen, die
       Nachfolger von  COMPARABLE  sind.

       Der erste genannte Fall der Zuweisung  x := y  bedeutet, da  x  nicht
       nur an Objekte der eigenen Klasse gebunden werden kann, sondern auch 
       an Objekte von Nachfolgerklassen.
       Diese vielleicht nicht sehr auffllig scheinende Regel ermglicht die
       DYNAMISCHE BINDUNG , und die ist eines der Grundprinzipien jeder ob-
       jektorientierten Programmierung.
       
       
       An einem Beispiel aus dem Handbuch kann dynamische Bindung demon-
       striert werden.

       POLYGON  sei eine Klasse, die Polygone beschreibt. Zu ihren features
       gehrt ein Array von Punkten, das die Ecken reprsentiert und ferner
       eine Funktion  
                                  perimeter , 
       die den Umfang des Polygon durch Summation ber die Eckenabstnde be-
       rechnet.
       Ein Nachkomme von  POLYGON  knnte sein:

       
       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

       end  -- class RECTANGLE


       Die Klasse  RECTANGLE  definiert die Umfangsfunktion neu, da der Um-
       fang bei Rechtecken natrlich effizienter ausgerechnet werden kann als 
       mittels der geerbten Implementation fr allgemeine Polygone.

       Die Klasse  RECTANGLE  knnte nun in folgender Weise von einem Eiffel-
       programm genutzt werden:

       p: POLYGON
       r: RECTANGLE
       ...
       !!p
       !!r
       ...
       if  some_test  then  p:=r  end
       print (p.perimeter)

       Die Zuweisung  p:=r  ist korrekt wegen der obigen Regel (Fall 1).
       Ist nun die Bedingung  some_test  falsch, so bleibt  p  an ein Objekt 
       vom Polygontyp gebunden, und die Berechnung von  p.perimeter  geschieht
       nach dem allgemeinen Polygon-Algorithmus.
       Im andern Fall jedoch wird  p  an ein Rechteck gebunden, und die Be-
       rechnung des Umfangs benutzt die fr Rechtecke neudefinierte Version.

       Ob nun die Gre  p  bei Laufzeit an ein Polygon oder speziell an ein
       Rechteck gebunden ist, kann aus dem Programmtext unmglich entnommen
       werden.
       Ob die Bedingung  some_test  in der if-Klausel wahr oder falsch ist,
       knnte zum Beispiel davon abhngen, welche Taste der Benutzer bei Lauf-
       zeit drckt.
       Der Feature-Aufruf
                                 p.perimeter
       besagt also, Zitat aus dem Handbuch: "Compute the perimeter of  p ,
       using the algorithm appropiate for whatever form  p  happens to have
       when the request is executed."
       Diese Mglichkeiten der dynamischen Bindung sind mit traditionellen
       Programmiermethoden nicht nachzubilden.
       In Pascal beispielsweise wre es ntig, alle nur denkbaren Flle fr
       p  in einer  if-  oder case-Anweisung einzeln aufzufhren. Kme dann
       ein weiterer, unvorhergesehener Fall hinzu, so knnte das tiefgreifen-
       de nderungen fr das gesamte Programmsystem erforderlich machen.
       
       In Eiffel wrde man lediglich (lokal) eine neue Klasse schreiben, die
       von POLYGON erbt und die den fr den neuen Fall geeigneten Algorithmus 
       von  perimeter  implementiert.

       Die an dem Beispiel demonstrierte Mglichkeit, eine Gre bei Laufzeit 
       an Objekte unterschiedlichen Typs zu binden, wird als  POLYMORPHISMUS
       bezeichnet.
       
       Eine Gre, die von einem allgemeinen Typ deklariert ist, kann bei 
       Laufzeit des Programms nur an Spezialisierungen (Nachfolgerobjekte) 
       des Typs gebunden werden, nicht aber an Vorgngerobjekte.
       Dies scheint intuitiv einleuchtend. Das Handbuch bringt folgende Ana-
       logie zur Illustration:
       Verlangt man im Restaurant eine Gemseplatte, so hat man, wenn man
       grnes Gemse bekommt, keinen Grund zur Beschwerde. Verlangt man jedoch
       grnes Gemse, so kann man mit gutem Grund die Gemseplatte zurckwei-
       sen, denn die knnte (beispielsweise) auch Karotten enthalten.

       
       Das folgende Beispiel aus dem Handbuch zeigt weitere Aspekte von Ver-
       erbung und dynamischer Bindung.
       Eine Kfz-Zulassung hat unterschiedliche Kfz-Typen zu versorgen.
       Vorbedingungen (Steuern bezahlt) und Nachbedingungen (bekommt sein
       Nummernschild) sind fr alle Typen gleich, jedoch ist der Registrier-
       vorgang unterschiedlich fr Pkw, Lkw, Motorrder etc.
       Fr die Zulassungsstelle wre dann die folgende Klasse ntzlich:

       
       deferred class VEHICLE
       feature
          dues_paid (year: INTEGER): BOOLEAN  is
             ...
             end 
          
          valid_plate (year: INTEGER): BOOLEAN  is
             ...
             end 
          
          register (year: INTEGER)  is
             --  Register vehicle for  year
             require
                dues_paid (year)
             deferred
             ensure
                valid_plate (year)
             end  
          ...
       end  --  class vehicle


       Die Klasse  VEHICLE  ist aufgeschoben, da die Prozedur  register  zwar
       deklariert, aber noch nicht definiert ist.
       Vor- und Nachbedingungen sind jedoch schon vorhanden, da die fr alle
       Kfz-Typen gleich sind.
       Effektive Versionen fr die Prozedur  register  fr den jeweiligen 
       Kfz-Typ enthalten erst die Klassen  CAR  und  TRUCK , die beide von  
       VEHICLE  erben:

                                  VEHICLE
                                /         \
                              CAR        TRUCK


       Die Zulassungsstelle kann diese Klassenstruktur in folgender Weise
       benutzen:

       v: VEHICLE
       c: CAR
       t: TRUCK
       ...
       !!c
       !!t  -- v kann nicht kreiert werden, da  class VEHICLE  aufgeschoben
            -- ist.  v kann aber zugewiesen werden wie folgt:
       if some_test  then  v := c  else  v := t  end
       v.register

       Auch hier ist aus dem Programmtext nicht zu ersehen, ob  v.register
       bei Laufzeit einen Pkw oder einen Lkw betrifft.
       
       Dieses Beispiel demonstriert die Ntzlichkeit von aufgeschobenen Klas-
       sen und dynamischer Bindung in der Design-Phase eines Eiffelsystems.
       Sie erste Version  VEHICLE  des Moduls ist aufgeschoben; sie enthlt 
       aber schon die Typstruktur (die "Architektur") und das, was von der 
       Semantik bekannt ist (ausgedrckt durch Vor- und Nachbedingungen).
       Die Entscheidungen ber konkrete Implementation der aufgeschobenen 
       features braucht erst spter zu erfolgen, also erst in Nachfolgerklas-
       sen und programmiertechnisch auch rein zeitlich.
       Mglichkeiten wie diese machen die Programmiersprache Eiffel auch zu 
       einer attraktiven PDL-Sprache (Program Design Language).


       Oben wurde die "Covariant typing policy" genannt: Bei Neudefinition 
       einer Routine in einer Nachfolgerklasse ist es legitim, den Typ eines 
       formalen Arguments zu ersetzen durch einen konformen Typ. 
       
       An einem weiteren Beispiel aus dem Handbuch soll Bedeutung und Proble-
       matik dieser Regel demonstriert werden.
         

         DRIVER             VEHICLE        register_driver (d:DRIVER) is ...
            |             /         \
       TRUCK_DRIVER      CAR      TRUCK    register_driver (d:TRUCK_DRIVER) is

       
       Diese Klassenstruktur ist ein Fall von paralleler Vererbungshierarchie.
       Offensichtlich besteht eine innere Zuordnung zwischen den Klassen
       DRIVER  und  VEHICLE  auf der einen Seite und zwischen  TRUCK_DRIVER
       und  TRUCK  auf der anderen Seite.
       
       Angenommen, die Klasse  VEHICLE  enthlt die Prozedur
                        register_driver (d:DRIVER) is ...
       (die etwa fr die Fhrerscheinerteilung wichtig sein knnte).
       
       Weil fr Lkw's ist eine andere Fhrerscheinklasse ntig ist, und weil
       Lkw's von Lkw-Fahrern gesteuert werden, wird die Klasse  TRUCK  die ge-
       nannte Prozedur umdefinieren und dabei den Typ des Arguments ndern
       wollen:
                       register_driver (d:TRUCK_DRIVER) is ...

       Dies entspricht er Covariant typing policy und ist offenbar auch unab-
       dingbar in realistischen Software-Anwendungen.
       
       Aber es kann Probleme geben.
       Angenommen, die obige Klassenstruktur wird woanders wie folgt benutzt:

       a:  VEHICLE
       t:  TRUCK
       dr: DRIVER
       ...
       !!t
       !!dr
       a := t
       ...
       a.register_driver (dr)   --  wobei also  dr  vom Typ  DRIVER  ist
                                --  (nicht vom Typ  TRUCK_DRIVER)

       Der Feature-Aufruf  a.register_driver (dr)  scheint ganz in Ordnung.
       Die Gre  a  ist vom Typ  VEHICLE , dort existiert die Routine
       register_driver , und der Typ des Arguments  dr  stimmt scheinbar
       auch.
       Aber wegen der dynamischen Bindung  a:=t  ist  a  an ein Objekt vom
       Typ  TRUCK  gebunden. In  TRUCK  sind aber nur Gren vom Typ
       TRUCK_DRIVER (oder konforme Typen) als aktuelle Parameter der Routine
       register_driver erlaubt, nicht aber die Gre  dr  (vom Typ  DRIVER).
       Mithin haben wir Konformitt in der "falschen" Richtung!
       
       Der obige Feature-Aufruf wre unzulssig, und mte vom Compiler als
       Fehler zurckgewiesen werden. Das Problem ist, da Fehler dieser Art
       nicht einzelne Klassen, sondern das Programmsystem als ganzes betref-
       fen und daher nicht einfach zu identifizieren sind.
       Bertrand Meyer bemerkt im Handbuch fr eine ltere Version des Eiffel-
       Compilers, da solche Fehler vom Compiler "selten" unentdeckt bleiben.


       ---------------------------------------------------------------------


       Wie in Teil 2 schon erwhnt, vereinfachen Ankertypen vor allem bei
       Vererbung die Syntax von Eiffelklassen und machen umstndliche Neude-
       finitionen berflssig.
       Unter dem Blickwinkel der dynamischen Bindung sind die Begriffe noch
       zu przisieren:

       Sei  x  eine Gre in der Klasse  C  und  anchor  eine andere Gre
       in  C .
       Durch die Deklaration
                                x : like anchor
       wird die Gre  x  in  C  so behandelt, als htte man ihr den Typ der
       Gre  anchor  gegeben.
       Wenn Nachkommen von  C  die Gre  anchor  neudeklarieren, so hat das
       automatisch die gleiche Neudeklaration fr  x  zur Folge.
       Man kann sagen: Der Typ von  x  folgt dem seines Ankers.
       
       Ein wichtiger Spezialfall ist es,  current  als Ankergre zu nehmen
       (vergl. die Diskussion der feature  infix "<="  und  copy  in Teil 2).
       
       Eine Gre  x  in  der Klasse  C  als
                               x : like Current
       zu deklarieren, bedeutet konsequenterweise, ihr in  C  den  Typ  C  zu
       geben und ihr den Typ  D  zu geben in einem Nachfolger  D  von  C .

       
       An drei weiteren Beispielen soll demonstriert werden, wie Ankertypen 
       benutzt werden knnen:
       
       Eiffel stellt die Funktion  
                                  clone (y)  
       bereit, welche ein neues Objekt zurckgibt, das Feld fr Feld identisch
       ist mit dem Feld, an das  y  gebunden ist.
       [ clone  steht in enger Beziehung zur Prozedur  copy , vergl. Handbuch
       S. 298-300. Auer mittels Kreieren ist es berhaupt nur mit  clone
       mglich, neue Objekte zu erzeugen. ]
       
       Mit  clone  hat man die Mglichkeit, Objekte beliebigen Typs zu repro-
       duzieren.
       Folglich kann in der Definition der clone-Funktion der Typ des Argu-
       ments  y  und auch der Rckgabetyp nur "beliebig" sein, d.h. vom Typ
       ANY  (wobei  ANY  eine Basisklasse in Eiffel ist, die automatisch an
       jede andere Klasse vererbt wird, vergl. Teil 4).
       Also liegt folgende Typvergabe nahe:
                        
                        clone (other: ANY): ANY  is ...        (ist falsch!!)
       
       Dann wre aber der folgende Standardgebrauch der clone-Funktion unmg-
       lich:

       x,y: SOME_TYPE
       ...
          --  Create an objekt and attach it to y:
       !!y
       ...
       x := clone (y)

       Dieses Fragment beabsichtigt, das mit  y  verbundene Objekt zu repro-
       duzieren und  x  daran zu binden. Der Ausdruck  clone (y)  wre jedoch 
       vom Typ  ANY  und der ist nicht konform zu  SOME_TYPE, dem Typ von  x. 
       Im Gegenteil ist  SOME_TYPE  konform zu  ANY . Die Konformittsrich-
       tung fr Zuweisungen wre gerade vertauscht!
       
       Die Lsung dieses Problems ist, das Resultat der clone-Funktion an den
       Typ des Arguments zu ankern:

                     clone (other: ANY): like other  is ...

       Jetzt funktioniert das obige Programmfragment.
       Der Aufruf  clone (y)  ist korrekt, da  SOME_TYPE  konform zu ANY ist. 
       clone (y)  ist nun vom Typ  SOME_TYPE , womit auch die Zuweisung  
       x := clone (y)  in Ordnung ist.

       
       Zweites Beispiel:
       Eine hnliche Beziehung wie zwischen  copy  und  clone  besteht zwi-
       schen den features  equal  und  is_equal  (vergl. Handbuch S.304).
       Die Standardfunktion
                equal (some: ANY , other: like some): BOOLEAN  is ...
       ankert das zweite Argument an das erste. Mit  equal  lt sich testen,
       ob zwei Objekte Feld fr Feld identisch sind.
       
       Es gibt in Eiffel nun einige Klassen (zum Beispiel die Standardklassen  
       ARRAY  und  STRING ) fr die eine genderte Version von  equal  gn-
       stiger ist als die Standardversion von  equal .
       Damit das funktioniert, bruchte man Aufrufe der syntaktischen Form
                                a.is_equal (b)
       wo dynamische Bindung dafr sorgt, da die aktuelle Variante des 
       Gleichheitstests genommen wird, falls bei Laufzeit die Gre  a  an 
       ein Objekt dynamisch gebunden ist, dessen Typ den Gleichheitstest um-
       definiert hat. 
       Die obige Funktion  equal  ist fr diese Art Aufruf aber nicht geeig-
       net und knnte stets nur den Standard-Gleichheitstest ausfhren, wie 
       er in der Klasse  GENERAL  implementiert ist.
       Dies ist der Grund (vergl. Handbuch S.304), warum es in Eiffel neben
       der Standardfunktion  equal  auch noch die Funktion
                  is_equal (other: like current): BOOLEAN  is  ...
       gibt, die die gewnschte Form des Aufrufs ermglicht.

       Das folgende Fragment stammt aus dem Programm  SORTRACE  aus Teil 2:

                       a1, a2 : ARRAY [STRING]
                       ...
                       a1.item (i).is_equal (a2.item (i))
       
       Hier werden die Strings  a1.item (i)  und  a2.item (i)  mit dem in der
       Klasse  STRING  adquat umdefinierten feature  is_equal  auf Gleichheit
       getestet.

       
       Das dritte Beispiel betrifft die parallelen Vererbungsstrukturen

                   COLLECTION                   CATALOG
                        |                          |
                      LIST                   LIST_CATALOG

       aus dem Container-Cluster von Eiffel/S und zeigt die Verbindung der
       geankerten Typdeklaration mit dem Prinzip der "Covariant typing
       policy".

       CATALOG [G, K->HASHABLE]  ist ein im Release 1.2 zu Eiffel/S neu hin-
       zugekommener Containertyp, der den Typ "Menge von Mengen" implemen-
       tiert, wo also die eingelagerten Objekte nicht einzelne Elemente, son-
       dern komplette Listen des Typs  COLLECTION [G]  sind.
       CATALOG  und  COLLECTION  sind aufgeschobene Klassen.
       Effektiv sind erst die Nachkommen, die sich durch den Typ der einge-
       lagerten Objekte unterscheiden. Einer dieser (insgesamt vier) effekti-
       ven Nachkommen ist  LIST_CATALOG , wo Listen vom Typ  LIST [G]  einge-
       lagert sind.
       CATALOG  hat nun eine Reihe von features, in denen die eingelagerten
       Objekte (also die Listen) angesprochen werden. Zum Beispiel

       at (k: K): ??
                --  die Liste, die unter dem "Schlssel"  k  gespeichert ist
       
       item (it: ITERATOR): ??
                --  die Liste, die dort gespeichert ist, wo der Iterator  it
                --  gerade steht

       store: ARRAY [??]
                --  das Array, in dem die Listen konkret gelagert werden
                --  (nach auen geheim, nur zum Zwecke der Implementation)

       Die Frage ist, welchen Typ braucht man fr  "??" ?
       Antwort: Es kommt darauf an, welche Version von  CATALOG  man gerade
       meint.
       Fr das feature  at (k: K): ??  beispielsweise bentigt man
       in  CATALOG        die Version   at (k: K): COLLECTION [G]   jedoch
       in  LIST_CATALOG   die Version   at (k: K): LIST [G]   
       und entsprechend fr alle anderen features und fr alle anderen Nach-
       folger der Klasse  CATALOG .
       
       Mit anderen Worten: fr jeden effektiven Nachkommen von  CATALOG  mu
       man eine Flle von features alle einzeln umdefinieren, was jedenfalls
       programmiertechnisch eine mhselige Angelegenheit ist.

       Aus diesem Grund haben die Autoren der Klasse  CATALOG  das Attribut
                             anchor: COLLECTION [G]
       hinzugefgt und smtliche anderen Gren dieses Typs an  anchor  gean-
       kert, also beispielsweise
                             at (k: K): like anchor
       In jeder Nachfolgerklasse von  CATALOG  reicht es nun, lediglich die
       Gre  anchor  umzudeklarieren, in  LIST_CATALOG  beispielsweise
                               anchor: LIST [G]
       Alle anderen an diesen Typ geankerten Gren wie etwa  
                            at (k: K): like anchor
                            item (it: ITERATOR): like anchor
                            store: ARRAY [like anchor]
       knnen bleiben, wie sie sind.
