Polymorphie, abstrakte Klassen und Schnittstellen


Mit diesem Übungsblatt werden wir das Vererbungskonzept weiter vertiefen und das Prinzip der Polymorphie erarbeiten. Wir nutzen in den folgenden Aufgaben extensiv das Upcasting, welches es erlaubt, einer Referenzvariable der Oberklasse Instanzen ihrer Unterklassen zuzuweisen. Hinsichtlich der Polymorphie sind die Unterklassen bei der Typprüfung kompatibel zur Oberklasse.

Aufgabe 1: Quizzes

Bearbeiten Sie zunächst die Quizzes zu Kapitel 51 und 52 in B. Kjells Java-Buch.

Aufgabe 2: Affenforscherin

Die Affenforscherin "Besine Gadegas" wandert durch den Dschungel am Fluss Okongo, um das Verhalten unterschiedlicher Affenarten zu erforschen. Immer, wenn sie den Schimpansen "Tschita" oder den Gorilla "George" besucht, kreischt der jeweils besuchte vor Freude und isst danach eine Banane. Zum Abschluss eines jeden Besuches trommelt george mit den Fäusten auf seiner Brust, wohingegen sich tschita in die Bäume hangelt.

  1. Implementieren Sie die Besuchssituation auf der Grundlage Ihrer Lösung zu der ersten Dschungelaufgabe vom letzten Übungsblatt an Hand einer Klasse Affenforscherin, die über eine Methode besucht (Affe a) verfügt, unter Ausnutzung von Polymorphie. Affenforscherinnen besitzen einen Namen.

    Rufen Sie alle Methoden, die Handlungen der Affen betreffen, in der Klasse Affenforscherin auf. Sie müssen hierfür Affen in Gorillas bzw. Schimpansen umwandeln können, was mit Hilfe expliziter Typecasts möglich ist. Ein Typecast für die Umwandlung einer Referenz a auf einen Affen in eine Schimpansen-Referenz s wird z. B. wie folgt notiert: Schimpanse s = (Schimpanse) a;

  2. Schreiben Sie ein Sketch-Hauptprogramm, in dem die Forscherin besine tschita und george besucht. Im Hauptprogramm finden keine Textausgaben statt.

  3. Ausgabe des Programms:

    Besine besucht den Schimpansen Tschita
    Tschita kreischt
    Tschita isst eine Banane
    Tschita hangelt
    Besine besucht den Gorilla George
    George kreischt
    George isst eine Banane
    George trommelt
    

Aufgabe 3: Buchladen

Ändern Sie Ihr Programm Buchladen aus dem letzten Übungsblatt in der folgenden Weise ab:

  1. Die einzelnen Buchtitel werden jetzt in einem Feld vom Typ Buch gehalten. Geben Sie die Buchdaten mit Hilfe einer for-Schleife aus.

  2. Ergänzen Sie ihre for-Schleife in der Weise, dass jeweils nach der Ausgabe der Buchdaten noch geprüft wird, welcher Klasse das Buchobjekt angehört. Geben Sie den Namen der Klasse aus, sodass Sie etwa folgende Ausgabe erhalten:

    Autor: R. Descartes, Titel: Meditationen, ISBN: n. v.
    Klasse: Buch
    
    Autor: Johann W. v. Goethe, Titel: Die Leiden des jungen Werther, ISBN: n. v., Genre: Liebesroman
    Klasse: Roman
    
    Autor: H. P. Gumm und M. Sommer, Titel: Einführung in die Informatik, ISBN: 3-486-25635-1, Fachgebiet: Informatik
    Klasse: Sachbuch
    
    Autor: W. Pschyrembel, Titel: Klinisches Wörterbuch, ISBN: 3-11-010881-255, Fachgebiet: Medizin
    Klasse: Medizinbuch
    

Aufgabe 4: Grafikobjekte

In dieser Übung werden Sie mit Hilfe der Grafikmethoden von Processing ein paar Grafikklassen implementieren und deren Instanzen im Grafikfenster von Processing zur Anzeige bringen. Hierfür benötigen Sie im Sketch-Hauptprogramm die setup()-Methode, wie auf der Vorlesungsfolie mit dem Titel "Grafikoperationen" im Abschnitt über die eingebetteten Klassen demonstriert.

  1. Erzeugen Sie zunächst eine neue Sketch-Datei und implementieren Sie das kleine Beispielprogramm von der Folie mit dem Titel "Grafikoperationen". Führen Sie es aus, um zu prüfen, ob soweit alles funktioniert.

  2. Implementieren Sie dann eine abgewandelte Form des Vererbungsbeispiels aus der Vorlesung bestehend aus der Oberklasse Shape mit den Unterklassen Triangle, Square und Pentagon.

    Abb. 1: Vererbungshierarchie

    Die Grafikmethoden von Processing funktionieren nur in Sketch-Dateien. Da wir in den Klassen zu diesen Grafikobjekten die Grafikmethoden von Processing benötigen, werden diese darum als innere Klassen in der Sketch-Datei programmiert, wie auf der Folie mit der Überschrift "Sketch-Datei mit innerer Klasse: Beispiel" demonstriert. D. h., es werden keine zusätzlichen Java-Dateien benötigt. Der gesamte Programmcode steht in der Sketch-Datei.

    Die Klasse Shape soll die folgenden Methoden zur Verfügung stellen:

    • Shape(float psize): Ein Konstruktor für das Setzen der Kantenlänge size der jeweiligen Figur gemäß Abb 2. Größen- und Koordinatenangaben werden unter Processing mit dem Datentyp float codiert.

    • void draw(float x, float y): Eine Methode für das Zeichnen der Figur. Für die allgemeine Figur Shape besitzt diese Routine einen leeren Methodenrumpf, da Shape für keine bestimmte geometrische Figur steht.

    In den Konstruktoren der Unterklassen wird der Konstruktor der Oberklasse verwendet. Die Methode draw(...) der Oberklasse wird in den Unterklassen überladen. Zu diesem Zweck besitzen die draw(...)-Methoden der Unterklassen einen Methodenrumpf mit dem Code für das Zeichnen der jeweiligen geometrischen Figur an der Position (x, y). Die Koordinaten der Kurvenpunkte können Abb. 2 unten entnommen werden.

    Abb. 2: Zu programmierende geometrische Figuren
  3. Erzeugen Sie in Ihrem Hauptprogramm von jedem dieser Grafikobjekte eines und speichern Sie diese unter Ausnutzung der Polymorphie in einem Feld des Typs Shape. Bringen Sie dann die drei Grafikobjekte mit Hilfe einer Schleife untereinander im Grafikfenster zur Darstellung, sodass Sie folgendes Ergebnis erhalten:

    Abb. 3: Inhalt des Grafikfensters
  4. Bis jetzt wurde die Methode draw(...) in der Klasse Shape mit leerem Methodenrumpf implementiert. Der Nachteil dieser Herangehensweise ist, dass mit ihrer Hilfe zwar eine Schnittstellenfunktion definiert, aber das Überladen der Methode in den entsprechenden Unterklassen nicht erzwungen wird. Verbessern Sie dies, indem Sie die Methode als abstrakte Methode deklarieren. Wenn Sie das Programm danach neu starten, sollte alles so funktionieren wie zuvor. Kommentieren Sie jetzt in einer Klasse, z. B. Triangle, die Definition der draw(...)-Methode aus und betrachten Sie die Reaktion der Entwicklungsumgebung im Editor. Was passiert? Warum?

  5. In der abstrakten Klasse Shape treten Schnittstelleneigenschaften zusammen mit einer Klassenvariablen für die Speicherung von Parameterwerten auf. Interfaces ermöglichen die reine Definition von Schnittstellen und sind immer dann sinnvoll, wenn es gilt, dass Verhalten einer Klasse festzulegen.

    Erweitern Sie ihren Klassenbaum um ein als Wurzel des Baums eingefügtes Interface IShape. Dieses deklariert die Methode draw(...). Implementieren Sie dieses Interface mit Hilfe der weiterhin abstrakten Klasse Shape.

Aufgabe 5: Erweiterter Container

Implementieren Sie eine erweiterte Form des generischen Containers aus der Vorlesung mit dem Namen Punkt, der nicht nur einen Datenwert, sondern drei Datenwerte speichern kann. Z. B. die x-, y- und z-Koordinate eines Punktes im Raum. Entsprechend müssen Sie den Konstruktor und die set-Methode mit mehr Parametern versehen sowie für jeden Parameter eine eigene get()-Methode implementieren (getX(), getY(), getZ()).

Testen Sie Ihre Implementierung mit jeweils drei Integer- und Float-Punkten sowie mit drei instanzierten LightBulb-Objekten. Diese werden zunächst jeweils in einem eigenen, an den jeweiligen Datentyp angepassten Container gespeichert und dann die in dem jeweiligen Container gespeicherten Daten mittels der get()-Methoden ausgelesen und auf die Konsole ausgegeben. Bei den Integer- und Float-Punkten sind dies die betreffenden Zahlenwerte, bei den Glühbirnen ihr Schaltzustand (ein- oder ausgeschaltet).

Begleitende Lektüre im Online-Kurs von Bradley Kjell:

Kapitel 51: "Abstract Classes and Polymorphism", Kapitel 52: "More about Polymorphism" und Kapitel 53: "Interfaces".

(Druckversion der Kapitel 50-54)