860 likes | 1.1k Views
Vererbung. Helmut Paulus Speyer, 7./8.11.2006. Teil 1. Grafik mit Delphi. TCanvas. Brush : TBrush Pen : TPen Font : TFont. moveto(x,y : integer); lineto(x, y : integer); ellipse(x1,y1,x2,y2 : integer) rectangle(x1,y1,x2,y2 : integer) polygon(p : array of Tpoint);
E N D
Vererbung Helmut Paulus Speyer, 7./8.11.2006
Teil 1 Grafik mit Delphi
TCanvas Brush : TBrush Pen : TPen Font : TFont ... moveto(x,y : integer); lineto(x, y : integer); ellipse(x1,y1,x2,y2 : integer) rectangle(x1,y1,x2,y2 : integer) polygon(p : array of Tpoint); TextOut(x, y: Integer; const Text: string); ... Zeichenfläche TCanvas Canvas-Objekte dienen als Zeichenfläche für grafische Elemente. Sie kapseln eine Vielzahl von Methoden zur Ausgabe von Grafik und Text in einem rechteckigen Bereich. TCanvas-WerkzeugePen, Brush und Font, zuständig für bestimmte Teilaufgaben: Pinsel – Füllfarbe, Muster ... Stift – Linienfarbe, Stil ... Schriftart – Farbe, Schrifttyp ... Der Koordinatenursprung (0,0) ist in der linken oberen Ecke einer Komponenten, die ein Canvas-Objekt zur Verfügung stellen.
Wie erhält man ein TCanvas-Objekt? TCanvas-Objekte werden von einigen Delphi-Komponenten als Property zur Verfügung gestellt: z. B.: Formular, Paintbox, Image, Bitmap • Der Koordinatenursprung ist die linke obere Ecke der jeweiligen Komponente. • Die positive y-Achse zeigt nach unten. • Die Image-Komponente speichert die Graphik zusätzlich in einer Hintergrundbitmap, so dass das Bild automatisch anzeigt wird, wenn das Formularfenster, nachdem es verdeckt war, wieder in den Vordergrund geholt wird. • Das Formular und Paintbox zeichnen das Bild nur einmalig. Damit es nach dem Verdecken wieder erscheint, muss das Zeichnen der Graphik in der OnPaint-Ereignismethode der jeweiligen Komponente erfolgen. Dieses Ereignis wird vom Betriebssystem automatisch ausgelöst.
Objekthierarchie Das Bild-Objekt Bild vom Typ TImage verwaltet über das Attribut Canvas ein Leinwand-Objekt der Klasse TCanvas, das wiederum ein Zeichenstift-Objekt der Klasse TPen und ein Malpinsel-Objekt der Klasse TBrush verwaltet. Die entsprechenden Attribute Pen und Brush werden als Property zur Verfügung gestellt. Beispiel: Blaues Rechteck Bild.Canvas.Brush.Color := clbue; Bild.canvas.rectangle(10,10,100,20); Pinselfarbe blau Rechteck mit der linken oberen Ecke am Punkt (X1, Y1) und der rechten unteren Ecke am Punkt (X2, Y2).
Aufgaben 1 • Testen Sie das Beispielprogramm Grafiktest • Variieren Sie verschiedene Modi der Pinsel und StifteTesten Sie insbesondere den NotXOR-Füllmodus • Das Programm soll wie folgt verbessert werden: • Nach Wahl Kreise, Rechtecke oder Polygone zeichnen • Position und Größe der Figur interaktiv durch Mausklicks bestimmen • Kreis: Kreismittelpunkt und Radius • Rechteck/Polygon: Eckpunkte
Figur auswählen Punkte bestimmen Figur zeichnen Zustandsbasierte Ablaufmodellierung Eine Figur soll erzeugt werden: Ablauf: Zustände:
FormMouseDown Punkt aufnehmen [nicht letzter Punkt] AuswahlBoxClick Punktearray inizialieren Z1: Figur gewählt Z0: Bereit FormMouseDown Figur zeichnen [letzter Punkt] Zustandsbasierte Ablaufmodellierung Zustandsautomat: Steuerung durch Ereignismethoden: AuswahlBoxClick : Figurtyp wählen FormMouseDown : Punkte speichern bis alle Punkte aufgenommen
Zustandsbasierte Modellierung Zustandsbasierte Modellierung ist bei der Entwicklung von Systemen immer dann sinnvoll, wenn das Verhalten eines Systems nicht nur von äußerenEingaben oder Ereignissen abhängt, sondern auch von inneren Zuständen, die das System in Abhängigkeit von den Eingaben bzw. Ereignissen durchläuft. Je nach Zustand kann ein Objekt anders auf dasselbe Ereignis reagieren. Ein Zustandsdiagramm ist immer genaueiner Klasse zugeordnet. Es beschreibt das dynamische Verhalten der Objekte. • In einem Zustandsdiagramm wird festgehalten: • auf welche interne und externeEreignisse ein Objekt reagiert, • wie ein Objekt reagiert und welche Aktivitäten ausgelöst werden, wenn es sich in einem bestimmten Zustand befindet, • in welchen Folgezustand ein Objekt übergeht als Reaktion auf ein Ereignis.
Implementierung Die gewählten Punkte werden gespeichert. Sind alle für die Figur notwendigen Punkte bestimmt, wird sie gezeichnet. type TZustand= (z0,z1); TGUI = class(TForm) procedure AuswahlBoxClick(Sender: TObject); procedure FormMouseDown(... X, Y: Integer); private { Private-Deklarationen} Zustand : TZustand; NMax : integer; Punkte : array of TPoint; procedure zeichneFigur; public { Public-Deklarationen} end; Typ der Zustandsvariablen mit den Konstanten z0, z1 Zustandsvariable Anzahl der Eckpunkte Dynamischer Array als Liste der Eckpunkte
Implementierung Figur auswählen im Zustand Z0 Punkte speichern im Zustand Z1 Procedure TGUI.FormMouseDown(...,X,Y:Integer); Var nr : integer; begin ifZustand = z1then begin // neuen Punkt dem Array hinzufügen nr := length(Punkte);//Punktzahl feststellen setLength(Punkte,nr+1);//Arraylänge erhöhen Punkte[nr] := Point(x,y);//Punkte zuweisen ifnr = NMax-1then// letzter Punkt? begin zeichneFigur; //Zustand z0 wiederherstellen Punkte := nil;//Punkte löschen Auswahlbox.ItemIndex := -1; Zustand := z0; end; end; end; procedure TGUI.AuswahlBoxClick(...); begin if Zustand = z0 then begin case AusWahlBox.ItemIndex of 0,1 : NMax := 2; 2 : NMax := einlesen end; zustand := z1; end; end;
Teil 2 Projekt Minigolf
Minigolfanlage Ziel Start Ziel: Ball ins Loch schlagen
Benutzer klickt aufs Spielfeld Ball rollt schrittweise in Richtung der Verbindungslinie Maus – Ballmitte Bei jedem Schritt wird seine Geschwindigkeit geringer Spielende, wenn Zielpunkt erreicht Modellierung der Animation Idee: Lösung: - Animationsschritte – timergesteuert (Ereignis: OnTimer) - Vermeidung von Flimmern durch Doppelte Pufferung • Doppelte Pufferung – Double Buffering: • Zusätzlich zum Bild auf dem Monitor wird noch ein Bild im Hintergrund gehalten.Hierzu wird ein zweites Canvas-Objekt für das Bild im Speicher benötigt. • Alle graphischen Ausgaben erfolgen zunächst im Speicherbild; anschließend wird dieses auf dem Monitor ausgegeben.
OOA: Analyse Fachkonzept KStart KZiel Kreisobjekte Ball GUI-Schicht Puffer Bitmap-Objekt Spielfeld Image-Komponente GUI Formular Steuerung Timer Animator
Beziehungen GUI-Schicht TForm ist ein 1 1 TGUI TImage TBitmap TTimer TCanvas Fachkonzept 2 benutzt 1 benutzt TKreis TBall
TKreis xPos, yPos //Mittelpunkt FFarbe, FRad Canvas //Ref. auf Zeichenfläche create paint moveto // an neue Position Modellierung der Klassen Klassen: TBall vx //Geschw. in x - Richtung vy //Geschw. in y – Richtung Zustand (liegt,rollt) ist ein rolle //verschieben um vx,vy setV getAbsV getZustand setZustand Objekte: instance of instance of KStart KZiel Ball
TKreis • xPos : integer • yPos : integer • FRad : integer • - FFarbe : TColor • - Canvas : TCanvas + Create(CanRef : TCanvas; x,y : integer) + paint + moveto(x,y : integer) + setAttribute(r : integer; f : TColor) Implementierung der Klasse „TKreis“ constructor TKreis.create(CanRef : TCanvas; x,y : integer); begin xPos := x; yPos := y; canvas := CanRef;// Anbindung an GUI end; procedure TKreis.paint; var x1,y1,x2,y2 : integer; begin //umgebendes Rechteck x1 := xPos-Frad; y1 := yPos-Frad; x2 := xPos+Frad; y2 := yPos+Frad; with canvas do begin Pen.Mode := pmNotXor; Pen.Color := clblack; brush.color:= FFarbe; ellipse(x1,y1,x2,y2); end; end; • Zeichnen im NotXOR-Modus ermöglicht Löschen der Figur • nicht bei Doppelter Pufferung
TKreis • xPos : integer • yPos : integer • FRad : integer • - FFarbe : TColor • - Canvas : TCanvas + Create(CanRef : TCanvas; x,y : integer) + paint + moveto(x,y : integer) + setAttribute(r : integer; f : TColor) Implementierung der Klasse „TKreis“ procedure TKreis.moveto(x,y : integer); begin //paint; //zum Löschen xPos := x; yPos := y; //paint;//erneut zeichnen end; procedure TKreis.setAttribute(r : integer;f : TColor); begin //paint; //zum Löschen FRad := r; FFarbe := f; //paint; //zum Löschen end;;
Implementierung der Klasse „TBall“ • Vererbung: • Die Klasse TKreis wird erweitert; dabei werden die Attribute und Operationen an die abgeleitete Klasse weitergegeben. type TZustand = (liegt, rollt); TBall = class(TKreis) //abgeleitet von TKreis private vx,vy : double; Zustand : TZustand; public procedure rolle; procedure setV(x, y : double); function getAbsV : double; function getVx : double; function getVy : double; function getZustand : TZustand; procedure setZustand(z : TZustand); end; TKreis ist ein TBall • vx : double • vy : double; • Zustand: TZustand + rolle; + setV(x, y : double); + getV : double; + getZustand : TZustand + getZustand
Implementierung der Klasse „TBall“ TKreis procedure TBall.rolle; begin ifZustand = rolltthen begin xPos := round(xPos + vx); yPos := round(yPos + vy); setV(0.8*vx, 0.8*vy); end; end; ist ein TBall • vx : double • vy : double; • Zustand : TZustand procedure TBall.setV(x, y : double); begin vx := x; vy := y; if getAbsV > 0.5 thenZustand := rollt elseZustand := liegt; end; function TBall.getAbsV : double; begin result := sqrt(vx*vx + vy*vy); end; + rolle; + setV(x, y : double); + getAbsV : double; + getZustand : TZustand + getZustand
V vy vx Modellierung des Ballabschlags Mausklick: Ball abschlagen Der liegende Ball, wird er durch Mausklick (x,y) abgeschlagen. Der Abschlag erfolgt in Richtung des Verbindungslinie Mauspunkt Ballmitte. Geschwindigkeitsvektor: vx = Ball.getx – x vy = Ball.gety – y (x,y) Animation-OnTimer: Ball.rolle wird in der Ereignismethode der Timerkomponente ständig ausgerufen. Die Position wird neu berechnet und die Geschwindigkeit verringert. Eine Zustandsänderung (liegt) erfolgt, wenn sie zu klein wird.
animiere rolle animiere Rolle SpielFeldMouseDown setV(vx,vy) liegt rollt animiere rolle [|v|< 0,5] v [im Ziel] v [außerhalb] Ball: Zustandsdiagramm [|v| > 0,5]
Modellierung des Spielablaufs Spielphasen: Anforderungen: • Bei Spielbeginn Ball auf Startposition • Kein Abschlag, wenn Ball im Loch oder außerhalb des Spielfeldes • Bahn/Spiel zu Ende, wenn Ball im Loch
animiere [eingelocht & alle Bahnen] Sp3 Sp2 BtnNextBahn Bahn aufbauen Ball auf Bahn Zustandsdiagramm Spielablauf: animiere SpielFeldMouseDown Ball.setV(vx,vy) SpU animiere [außerhalb] BtnStartPosClick Ball an Start BtnStartPosClick Ball an Start Strafpunkte Sp1 Sp0 animiere [eingelocht] In der einfachen Version (nur eine Spielbahn) entfallen die Zustände Sp2 und Sp3
Zeichnen im Hintergrund: • Puffer.canvas.ellipse(...); 2. Hintergrundbild ins Spielfeld kopieren: Spielfeld.canvas.draw(0,0,Puffer) Doppelte Pufferung Monitorbild Spielfeld : TImage Graphikausgabe in zwei Schritten: Spielfeld.canvas Puffer.canvas Bei der Animation von bewegten Objekten wird das Hinter-grundbild vor jeder Änderung gelöscht, indem die Zeichen-fläche mit der Hintergrundfarbe gefüllt wird: Puffer.Canvas.fillRect(...); Hintergrundbild Puffer : TBitmap
Implementierung: Doppelte Pufferung Idee: AlleGraphikausgaben erfolgen zunächst in ein Hintergrundbild, das dann als Ganzes in eine sichtbare Zeichenfläche kopiert wird. Einrichten des Puffers (OnCreate) var Puffer : TBitmap; //enthält das Hintergrundbild procedure TFenster.FormCreate( ... ); begin Puffer := TBitmap.Create; Puffer.Width := SpielFeld.Width; Puffer.Height := SpielFeld.Height; ... StartPkt := TKreis.create(Puffer.canvas,StartX,StartY); ZielPkt := TKreis.create(Puffer.canvas,400,100); Ball := TBall.create(Puffer.canvas,50,50); ... end; Bitmap-Objekt erzeugen Größe des Puffers anpassen Zeichenfläche des Puffers an Graphik-Objekte übergeben
Implementierung: Doppelte Pufferung Hintergrundbild aufbauen und auf dem Monitor ausgeben procedure TFenster.zeichneSpielFeld; begin //altes Bild löschen (mit Hintergrundfarbe füllen) Puffer.Canvas.Brush.Color := SpielFeld.Canvas.Brush.Color; Puffer.Canvas.FillRect(rect(0,0,Puffer.width,Puffer.height)); //Graphische Objekte neu zeichnen im Hintergrund StartPkt.paint; ZielPkt.paint; Ball.paint; //Hintergrundbild in Zeichenfläche des Spielfeldes kopieren SpielFeld.Canvas.Draw(0,0,Puffer); end; Die Formular-Methode zeichneSpielfeld enthält alleGraphikbefehle. Sie wird nach jeder Aktualisierung eines Graphikobjekts aufgerufen. Benutzt man das Canvas-Objekt des Formulars alsMonitorzeichenfläche, so kann das Zeichnen durch das OnPaint-Ereignis des Formulars ausgelöst werden.
Aufgaben 2 • Programm Minigolf testen. • Programm so erweitern, dass Spielstände angezeigt werden.- pro Schlag ein Punkt- 2 Strafpunkte, wenn Ball von außerhalb zurückgeholt wird • Reflexion an den Rändern des Spielfeldes • Entwicklung eines magischen Feldes (Objekt), das den Ball vom Spielfeld schleudert, sobald er es betritt.
Teil 3 Modellierung grafischer Objekte
Anwendungsbeispiel Aktivitäten: verschieben, drehen, skalieren, editieren
Anforderungen • Erzeugung und Verwaltung von grafischen Objekten unter-schiedlicher Form • Fähigkeiten der Objekte • frei verschiebbar (Maus) • skalierbar • drehbar
Erste OOA-Analyse TKreis TQuadrat TText TDreieck ... TRechteck TParallel • Problem:Vielfalt der Objekte • Optimierung: Entwicklung einer Klassenhierarchie
TGrafikElement TKreis TPolygon TLinie TText TRegNEck TParallel TRechteck Klassenhierarchie
Klassenhierarchie Optimierung der Klassen durch Entwicklung einer Vererbungsstruktur • Ziele: • Der gemeinsame Kern aller Elemente wird in einer Basisklasse modelliert. • Spezialisierte Klassen werden von der Basisklasse abgeleitet;sie fügen spezielle Attribute und Operationen hinzu oder definieren bestimmte Dinge neu.
Klassenhierarchie • Vorteile: • Durch Vererbung werden alle gemeinsamen Eigenschaften und Operationen an abgeleitete Klassen weitergegeben: • Grundlegende Attribute und Operationen werden nur einmal entwickelt • Möglichkeit zur Anpassung und Erweiterung unter geringem Aufwand (Wiederverwendbarkeit) • Einheitlicher Zugriff auf verschiedene Objekttypen wird möglich(Polymorphismus)
Einfaches Beispiel Kreise und Rechtecke
TKreis TRechteck xPos, yPos //Mittelpunkte FFarbe // Füllfarbe Canvas //Zeichenflächenref. Markiert // Markierungsflag FBreite, FLaenge xPos, yPos //Mittelpunkte FFarbe // Füllfarbe Canvas //Zeichenflächenref. Markiert // Markierungsflag FRad //Radius Create paint Moveto // verschieben skalieren //vergrößern Create paint Moveto // verschieben skalieren //vergrößern Kreise und Rechtecke Beide Klassen besitzen gleiche Attribute und Methoden gleicher Funktionalität. Das Gemeinsame muss nicht mehrmals implementiert werden!
Die Basisklasse enthält den gemeinsamenKern aller grafischen Objektklassen. Basisklasse Generalisierung TGrafikElement xPos, yPos //Mittelpunkte FFarbe // Füllfarbe Canvas //Zeichenflächenref. Markiert // Markierungsflag Create //Basiskonstruktor Moveto // verschieben TKreis ist ein ist ein FRad //Radius abgeleitete Klassen (Unterklasse) TRechteck Spezialisierung paint skalieren //vergrößern FBreite, FLaenge paint skalieren //vergrößern Erste Modellierung der Klassen
TGrafikElement - xPos : integer - yPos : integer - FFarbe : TColor - Fmarkiert : boolean - Canvas : TCanvas + Create(CanRef : TCanvas; x, y : integer) + moveto(x, y : integer); + getX : integer + getY : integer Implementierung der Basisklasse • TGrafikElement = class • private • xPos : integer; • yPos : integer; • FFarbe : TColor; • Fmarkiert : boolean; • Canvas : TCanvas; • public • constructor create(CanRef : TCanvas; x,y : integer); • procedure moveto(x,y : integer); • function getX : integer; • function getY : integer; • getFarbe Farbe : Tcolor; • procedure setFarbe(wert : TColor); • end;
TGrafikElement - xPos : integer - yPos : integer - FFarbe : TColor - Fmarkiert : boolean - Canvas : TCanvas + Create(CanRef : TCanvas; x, y : integer) + getX : integer + getY : integer Implementierung der Basisklasse • constructor TGrafikElement.create(CanRef:TCanvas; • x,y : integer); • begin • xPos := x; • yPos := y; • canvas := CanRef; • End; Die Objekte erhalten eine Referenz auf das Zeichenflächenobjekt des GUI-Objekts procedure TGrafikElement.moveto(x,y : integer); begin xPos := x; yPos := y; end;
TGrafikElement • ... • ... TKreis - FRad : integer - setRadius(wert : integer) + paint + setAttribute( r : integer, f : TColor) „Ist ein“ - Beziehung Die Unterklassen beschreiben nur die Abweichungen von von der Oberklasse. Alles andere kann wiederverwendet werden, weil es in der Oberklasse vorliegt. Die Methodenund Attributeder Oberklasse bilden eine Teilmenge der Methoden und Attribute der Unterklasse. Ein Objekt der Unterklasse hat somit ein Objekt der Oberklasse als Teilobjekt. Umgekehrt sind alle Objekte der Unterklasse eine Teilmenge der Objekte der Oberklasse. ist ein Jeder Kreis ist ein GrafikElement Konsequenz: Zuweisungskompatibilität Einer Variablen vom Typ der Oberklasse kann eine Referenz auf ein Unterklassenobjekt zugewiesen werden.
TGrafikElement • ... • ... TKreis • FRad : integer • ... + paint + setAttribute( r : integer, f : TColor) + getRadius : ineger + setRadius(wert : integer) Implementierung der Klasse „TKreis“ TKreis = class(TGrafikElement) private Frad : integer; public constructor create(CanRef : TCanvas; x,y : integer); procedure paint; procedure setAttribute(r : integer;f : TColor); function getRadius : integer; procedure setRadius(wert : integer); end; ist ein
TGrafikElement • ... • ... TKreis - FRad : integer - setRadius(wert : integer) Durch Aufruf des geerbten Konstruktors werden die Attribute des Basisobjekts initialisiert.Das abgeleitete Objekt enthält quasi ein Basisobjekt als Teilobjekt. + paint + setAttribute( r : integer, f : TColor) Implementierung der Klasse „TKreis“ constructor TKreis.create(CanRef : TCanvas; x,y : integer); begin inheritedcreate(CanRef,x,y); //Konstruktor der Oberklasse FRad := 10; end; ist ein
TGrafikElement • ... • ... TKreis - FRad : integer + paint + setRadius(wert : integer) + setAttribute( r : integer, f : TColor) Implementierung der Klasse „TKreis“ procedure TKreis.paint; var x1,y1,x2,y2 : integer; begin //umgebendes Rechteck x1 := xPos-Frad; y1 := yPos-Frad; x2 := xPos+Frad; y2 := yPos+Frad; with canvas do begin brush.color:= FFarbe; ellipse(x1,y1,x2,y2); end; end; ist ein procedure TKreis.setAttribute(r : integer;f : TColor); begin FRad := r; FFarbe := f; end;
Vererbung und Zugriffrechte Elemente einer Klasse können in der Klassendeklaration vor dem Zugriff von außen geschützt werden (Geheimnisprinzip). Schutzebenen: • publickeine Zugriffsbeschränkung nach außen • privateZugriff nur innerhalb einer Klasse • protectedZugriff auch in abgeleiteten Klassen möglich Die Schutzebenen private und protected gelten allerdings nur für den Zugriff aus einer anderen Unit heraus. Vererbung der Zugriffsrechte Die Zugriffsrechte einer Klasse werden an abgeleitete Klassen vererbt.Schutzrechte können in einer abgeleiteten Klasse abgeschwächt, aber nicht verschärft werden.
Zuweisungskompatibilität Konsequenz der „ist ein“ – Beziehung:Einer Objektvariablen einer Oberklasse können Objekte aller Unterklassen zugewiesen werden. Die Umkehrung gilt nicht. • Beispiel: • Var • Kreis : TKreis; • Rechteck : TRechteck • Grafik : TGrafikElement; ... • Kreis := TKreis.create(...); • Rechteck := TRechteck.create(..); • ... Möglich: Grafik := Kreis; Grafik.setFarbe(clred); Grafik := Rechteck; Nicht möglich: Grafik.setAttribute(...) Grafik.setBreite(100); Beachte: Grafikist eine Referenz auf den Teil des Kreisobjekts, der von TGrafikElement geerbt wird.
Operatoren „is“ und „as“ • is- Operator : Typprüfung (Laufzeit) • Beispiel: • Var • Figur : TGrafikElement; • Kreis : TKreis; • ... • if Figur is TKreis then • TKreis(Figur).paint; Typumwandlung (type cast) • - lieferttrue , wenn das Objekt Figur eine Instanz der angegebenen Klasse oder eines ihrer abgeleiteten Klassen ist • as - Operator : Typumwandlung (Laufzeit) • Beispiel: • Kreis := Figur as TKreis; • (Figur as TKreis).paint; • - liefert eine Referenz auf ein Objekt vom des Objektes Kreis
Statische Methoden Die bisher definierten Methoden sind statisch: Beim Compilieren ist der Objekttyp bekannt, sodass die Sprungadresse der Methode festgelegt werden kann (statische Bindung) Kreis.paint; //paint - Methode des Kreisobjekts Rechteck.paint; //paint - Methode des Rechteckobjekts Der Typ der Referenz auf das Objektbestimmt, welche Methode aufgerufen wird. Problem: Dieses Verhalten ist, wenn Vererbung ins Spiel kommt, oft unerwünscht.Z. B. wenn sich erst zur Laufzeit entscheidet, welcher Objekttyp angesprochen wird.
Statische Methoden • Beispiel: • Zur Laufzeit werden verschiedene Grafikobjekte erzeugt und ihre Referenzen in einem Array als Container verwaltet: • Var Figur : array[1..10] of GrafikElement; • Ziel: Einheitlicher Zugriff auf bestimmte Methoden, wie z. B.: • for i := 1 to 10 do • Figur[i].paint; Da zur Compilierzeit noch nicht feststeht, welche Objekttypen ins Array aufgenommen werden, entscheidet sich erst zur Laufzeit, welche Objektmethode aufgerufen werden soll. Dazu müsste der aktuelle Objekttyp für die Auswahl der Methode maßgebend sein.