2 Polymorphe Typsysteme alp3-2.1 1 Zur Erinnerung (Programmiersprachen-Terminologie): $ Vereinbarung (declaration): ein benanntes Programmelement wird eingeführt. $ Dessen Name (identifier) soll die eindeutige Bezugnahme auf dieses Programmelement ermöglichen (innerhalb seines Sichtbarkeitsbereichs!). $ Konsequenz: Verbot von Namenskollisionen. Abschwächung dieses Verbots  Polymorphie alp3-2.1 2 Polymorphie (polymorphism; griech. „Vielgestaltigkeit“) ist Eigenschaft einer Programmiersprache bzw. ihres Typsystems: Bei einem Prozeduraufruf (Methodenaufruf, Funktionsaufruf, ...) werden zur Identifikation der Prozedur neben dem Namen auch die Typen der involvierten Daten herangezogen. alp3-2.1 3 Polymorphie Überladen (overloading) ad-hoc (Java, ...) alp3-2.1 geplant (Haskell, ...) Universelle Polymorphie (universal polymorphism) parametrische P. Einschluss-P. (parametric p.), (inclusion p.) (Haskell, Java, ...) (Java, ...) Generizität bei Vererbung 4 2.1 Überladen (vgl. ALP II - 6.2) Beispiele: 1234 + 4711 "über" + "laden" 1000 + "fach" Addition Konkatenation Konkatenation nach Typanpassung boolean big(int i) {return Math.abs(i)>=10000; } boolean big(String s){return s.compareTo("zzzzz")>0;} ... big("123") || big(123) ... void test(A a) { ... } void test(B b) { ... } ... test(new A()); test(new B()); ... alp3-2.1 5 Signatur eines Klassenelements: - Name bzw. - Name samt Anzahl und Typen der Argumente (bei Methode) Beispiel: float power(float x, int i) { ... } hat Signatur power(float,int) Elemente einer Klasse müssen verschiedene Signaturen haben. (Diese Regel schwächt das Kollisionsverbot für Namen ab.) Überladen (overloading) eines Namens liegt vor, wenn es mehrere gleichnamige Elemente mit verschiedenen Signaturen gibt. Achtung: nicht verwechseln mit Ersetzen (overriding) in einer Unterklasse! (ALP II - 10.3) alp3-2.1 6 Beispiel - die folgende Klasse ist fast korrekt: class static static static static static } neg { int neg = -1; void neg(){neg = neg(neg);} int neg(int x){return -x;} int neg(float x){return -(int)x;} float neg(float x){return -x;}// collides ? Welches Element ist gemeint, wenn der Name neg benutzt wird ? Z.B. ... neg = neg(neg); ... alp3-2.1 ?? 7 ... neg ... ? 1. Überladen wird statisch (!) aufgelöst: Falls keine öffnende Klammer folgt: Variable. [Fehler, falls keine solche Variable sichtbar.] 2. Sonst bei n Argumenten: Methode mit n Argumenten und passenden* Argumenttypen, sofern es genau eine solche gibt. [Fehler, falls keine solche Methode sichtbar.] 3. Falls mehrere solche: die „spezifischste Methode“ (d.h. verwirf jede solche Methode, zu der es eine andere solche Methode gibt, deren Argumenttypen verträglich mit den Argumenttypen jener Methode sind). [Fehler, falls diese Methode nicht eindeutig bestimmbar.] alp3-2.1 * Typverträglichkeit! 8 Beispiel 1: ... static void neg(){neg = neg(neg);} ... int neg (1.) int neg(int x) Beispiel 2: n void m(short s, int i) o void m(int i,  n m(by,in)  o m(in,ch) char c) (3.) int short char byte  falsch, weil weder n noch o passt (2.) m(by,ch)  falsch, weil sowohl n als auch o passt (3.) m(ch,by) alp3-2.1 9 Zur Erinnerung: Überladen in Haskell ? $ mit jedem Funktionsnamen ist ein bestimmter Funktionstyp - die Signatur - verbunden (evtl. polymorph!) $ die Signatur beinhaltet auch den Ergebnistyp $ ad-hoc-Überladen wie in Java ist nicht vorgesehen $ aber eingeschränkt geplantes Überladen mit Typklassen Beispiel: power :: Double -> Int -> Double power x i = if i==0 then 1 else x * power x (i-1) kollidiert mit power x i = x ++ show i (Signatur Show a => [Char] -> a -> [Char] ) alp3-2.1 Was tun? 10 class Power t where power :: t -> Int -> t ( t ist „formaler Typparameter“ ) instance Power Double where (... hier durch Double aktualisiert) power x i = if i==0 then 1 else x * power x (i-1) data Text = Str [Char] deriving Show instance Power Text where (... hier durch Text aktualisiert) power(Str x)i = Str(x ++ show i) ( alp3-2.1 power(Str "abc")3 liefert Str "abc3" ) 11