Typverträglichkeit bei Feldern mit Klassentyp als Basistyp zur Erinnerung: short [] aShort={1,2,3}; int [] aInt; //aInt = aSort; // Typfehler Aber B Subtyp von A , dann B[] Subtyp von A[] Problematisch: Account[] a = new Account[10]; Checking[] c = new Checking [5]; a=c; a[0]= c[0]; a[0] = new Account(4999); bedeutet? Exception in thread "main" java.lang.ArrayStoreException: hs / fub - alp2-08 43 Sichtbarkeit, Verdecken und Ersetzen Sichtbarkeit: definiert durch Modifikatoren und Vererbung Sichtbarkeitsmodifikatoren (Klassen): keiner Klasse ist nur aus demselben package erreichbar. public Klasse ist von überall erreichbar. final Klasse ist final, d.h. kann nicht vererbt werden. Es können keine Unterklassen abgeleitet werden. (Eigentlich kein Sichtbarkeitsmodifikator: public final überall sichtbar und nicht erweiterbar) hs / fub - alp2-08 44 Sichtbarkeitsmodifikatoren für Attribute und Methoden: keiner Variable ist nur innerhalb der eigenen Klasse und des package erreichbar, zu dem sie gehört. public Variable ist überall dort erreichbar, wo auch die Klasse erreichbar ist, zu der sie gehört. private Variable ist nur innerhalb der eigenen Klasse erreichbar. protected Variable ist nur innerhalb der eigenen Klasse und des package erreichbar, zu dem sie gehört, ferner in Unterklassen. hs / fub - alp2-08 45 Sichtbarkeit verdeckender oder überschreibender Namen darf nicht gegenüber den verdeckten / ersetzten verringert werden. Deshalb nur folgende Änderungen erlaubt: protected leer → public → public, protected … aber hs / fub - alp2-08 46 … eine gewisse Vorsicht ist angebracht: class C1 { private int foo() {return int bar(){ return foo(); } } 0;} überschreibt private foo() class D1 extends C1 { int foo(){return 1;} public static void main(String[] args) { D1 c = new D1(); System.out.println(c.bar()); wird geerbt und bezieht } } auf private foo(), … und ohne private? s.u. sich was nicht direkt verwendet werden darf! hs / fub - alp2-08 47 Verdecken (hiding) und Ersetzen (Überschreiben, overriding) Betrachten hier nur vertikale Kollision in der Vererbungshierarchie • Attribute: Verdecken wie in Blockstruktur (hiding) (aber gleiche Namen/Typ erlaubt.) • Methoden: verschiedene Signatur: gleiche Signatur, beide statisch gleiche Signatur beide nichtstatisch sonst Überladen (overloading) Verdecken Ersetzen (overriding) Fehler (*) Implementieren von mehr als einer Schnittstelle kann auch zu Namenskollision führen. hs / fub - alp2-08 48 Verdecken von Attributen (1) class B extends A { int bool=1; ... } class A { boolean bool=true; ... } B b = new B(); A a = b; a.bool=false; System.out.println(a.bool + ((B)a).bool); > false > 1 b bool: false a statischer Typ von a ist A, dynamischer B ⇒ Statischer Typ bestimmt Attribut! hs / fub - alp2-08 49 Verdecken von Attributen (2) Auch statische Attribute werden verdeckt: class C { boolean bool=false; static int i=10; class D extends C { static int bool; // hides boolean boolean i; // hides static int i static void op(){ System.out.println ("op() in C: i="+i); //... }} static void op(){ // i=1; //can only use static //members! but i is now dynamic System.out.println("op() in D: bool=:"+bool); }} D dd = new D(); C c = dd; // c.bool= 1; error: static type of c.bool is boolean c.bool = true; dd.bool = 1; ((D)c).bool = 22; c.i = 11; hs / fub - alp2-08 50 Statischer Typ bestimmt Attribut! d D d = new D(); C c = d ; // c.bool= 1; error c.bool = true; d .bool = 1; ((D)c).bool = 22; c.i = 11; d.i = true; D e = new D(); c bool i false true true C D static fields 10 11 i bool e bool 221 false i hs / fub - alp2-08 51 Verdeckung statischer Methoden … wie bei Attributen: der statische Typ bestimmt die auszuführende Methode. D dd = new D(); C c = dd; c.op(); ((D)c).op(); dd.op(); Ausgabe: > op() in C: i= 11 > op() in D: bool=22 > op() in D: bool=22 class C { boolean bool=false int i=11; static void op(){ System.out.println ("op() in C: i="+i); …} class D extends C { static int bool = 22; static void op(){ System.out.println ("op() in D: bool="+bool); } hs / fub - alp2-08 52 Polymorphie: Methoden gemäß dynamischem Typ ausführen Class Saving extends Account{ … void compInterest(){ … //for saving accounts } Class Checking extends Account{ … void compInterest(){ … //for checking accounts! } Account a = new Saving(..); a.compInterest(); a = new Checking(..); a.compInterest(); Zur Laufzeit (dynamisch) wird die Methode gewählt, die das Objekt besitzt, auf die a verweist, nicht die durch den statischen Typ (hier Account) von a bestimmte. hs / fub - alp2-08 53 Po·ly·mor'phie, die; -, keine Mehrzahl 1.Vielgestaltigkeit, Verschiedengestaltigkeit 2. […] (*) Diese Art der Polymorphie ist nur im Zusammenhang mit Vererbung definiert. - Vererbung von Klassen, - Vererbung von Schnittstellen, - Implementierung von Schnittstellen durch Klassen. -Sammelbegriff: Inklusions- (Einschluss-) Polymorphie (inclusion polymorphism) Andere Arten von Polymorphie?? (*) Langenscheidt Fremdwörterbuch (digitale Ausgabe) hs / fub - alp2-08 54 Polymorphie in Java Zugriffsregel: • für nichtstatische Methoden ist der Typ des Objekts entscheidend • für Attribute und statische Methoden der Typ des der Verweisvariablen (bzw. Parameter) Grund dafür: Effizienter Attributzugriff - schon beim Übersetzen steht die Speicheradresse fest - Adressen der Methoden werden sowieso Nicht in allen dynamisch ermittelt OO Sprachen so. Regeln können explizit umgangen werden Regeln beachten! z,B, durch Typanpassung. hs / fub - alp2-08 55 Dynamischer Methodenaufruf c Rechteck a Felder a,b,.. b Felder a,b,.. flaeche() static farbe() ... f 2DGeoO Referenzen Objekte (nur Werte!) Klasse d Felder r,.. e Genaugenommen: KlassenDeskriptoren, die die Adressen der Methoden enthalten Das ist die Kreisfläche! also: völlig anderer Code g Kreis Code für diese flaeche() Felder r,.. flaeche() farbe() ... Code farbe() Code für jene flaeche() Zur Übersetzungszeit ist nicht bekannt, ob f bzw. g auf ein Rechteck oder einen Kreis zeigen. Stattdessen wird g.flaeche() so ausgeführt: (1) Klassendeskriptor lesen (2) dort Adresse a von „richtiger flaeche()“ finden hs / fub - alp2-08 56 Generizität (*) Unterschied zu Inklusionspolymorphie? Vergleiche Generizität in Haskell! Generizität = parametrische Polymorphie Typparameter ermöglichen das Schreiben von Programmen ohne auf einen Typ oder die Vererbungsbeziehung von Typen festgelegt zu sein. Vor Ausführung des Programms: Typparameter durch konkreten Typ ersetzen. (*) hier nur extrem knapp behandelt. Details ALP3 bzw. Literatur hs / fub - alp2-08 57 Zwei Beispiele: a) Für Algorithmus ist der konkrete Typ belanglos append :: [a]-> [a] -> [a] append [] xListe = xListe append (y:yListe) xListe = x:(append yListe xListe) b) Konkrete Typen müssen zueinander passen, welche das sind spielt keine Rolle. map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xList) = (f y): map f xList Objektorientierte Sprachen? hs / fub - alp2-08 58 Generizität .. gibt es in Eiffel, C++ ("Template-Mechanismus"), C# (seit .NET 2.0) und Java (seit 1.5) Generische verkettete Listen in Java: public class LListGeneric <G>{ private ListNodeGen <G> head, tail, current; private ListNodeGen <G> sentinel = new ListNodeGen<G>(null); … public void add(G o){ // insert at front head = new ListNodeGen <G>(o,sentinel.next); sentinel.next=head; size++; typarametrisierte Methode } … } hs / fub - alp2-08 59 Verwendung von generischen Listen map (2*) [1..10] Typparameter a, b durch integer ersetzen. Java: public static void main(String[] args) { LListGeneric<Integer> ll; int maxElem = 17; ll= new LListGeneric<Integer>(); for (int i=1;i<=maxElem;i++) ll.add(new Integer(i)); } hs / fub - alp2-08 60 Problematisch… … ist die Interferenz mit Vererbung. Zum Beispiel: Aus B verträglich mit A folgt nicht List<B> verträglich mit List<A> List<A> a = List<B> b = a.head = new a.head = new new List<A>(); new List<B>(); A(); B(); // vgl. Felder Komplexität des Typsystems erhöht sich deutlich. hs / fub - alp2-08 61 Übersicht Polymorphie Polymorphie Überladen ad hoc Typwandlung (coercion) Universelle Polymorphie Generizität Inklusionspolymorphie hs / fub - alp2-08 62 Ergänzung (*) Innere Klassen Manchmal nützlich, Klassen Inside nicht auf dem "Toplevel" zu definieren, sondern innerhalb einer anderen Klasse Outside. 1. statische innere Klassen class A{ static class B{... } } Kein Problem: B ist Attribut von A mit Zugriff auf alle statischen Eigenschaften von A (aber nicht die dynamischen) (*) in der Literatur nachlesen hs / fub - alp2-08 63 2. Elementklassen class A{ class B{... } B b = new B(); } A a = new A(); B b = a.new B(); B ist Attribut von A das erzeugt werden muss und Zugriff auf die nichtstatischen Eigenschaften von A hat. Von "außen" kann ein B-Objekt nur erzeugt werden, wenn ein zugehöriges A-Objekt a existiert. hs / fub - alp2-08 64 2. Lokale und anonyme innere Klassen class A{ public static void main( String args[] ) { Point p = new Point( 10, 12 ) { public String toString() { return "(" + x + "," + y + ")"; } }; System.out.println( p ); // (10,12) } } Hier wird ein einziges Objekt erzeugt, das über p verwendet werden kann. new A {<anonyme Klasse>}: anonyme ist Unterklasse von class A - im Beispiel: {…} überschreibt toString() – Wenn interface A (Schnittstelle), wird sie von {...} implementiert. Variante: benannte lokale Klasse: {..class B{...}new B; hs / fub - alp2-08 65 }