Kü /Info Oberstufe OOP - Raumplanung Sj. 2012/2013 Was ist Vererbung und warum sollte man das verwenden? Video: o Video „OOP - Vererbung“ - http://youtu.be/vEKnrRfIJLw o Vererbung.wmv Es ist sehr unbefriedigend, große Programmtextabschnitte durch Kopieren in andere Klassen zu übertragen, ohne dass in weiten Teilen auch nur eine Zeile geändert wird (will man nachträglich z.B. dort etwas ändern, so muss man das an vielen Stellen tun und verliert schnell den Überblick). Absolut naheliegend ist, diese Abschnitte in eine eigene „Datei“ auszugliedern, um dann jedes Mal darauf zuzugreifen (oder dafür eigene Prozeduren zu benennen). In der OO ist das geeignete Mittel dafür aber keine Datei, sondern eine neue Klasse. Das ist aber nicht nur einfach eine andere Schreibweise, dahinter steckt viel mehr – es geht um eine neue Art von Abstraktion. Beispiel: Die Möbelklassen bisher beschreiben konkrete Objekte, nun wird aber von allen Möbelklassen eine übergeordnete Klasse Moebel geschaffen, die alle Attribute und Methoden enthält, die die nun untergeordneten konkreten Möbelklassen gemeinsam hatten. Diese übergeordnete Klasse Moebel beschreibt selbst kein konkretes Objekt (was genau sollte den ein Moebel auch sein ), sie ist wie eine Art „Sammeleimer“ für alle Attribute und Methoden, die die Unterklassen gemeinsam brauchen. Dadurch muss man die Attribute und Methoden nun nur noch einmal in der Oberklasse schreiben und die Unterklassen können dann automatisch darauf zugreifen (mit einer Einschränkung: die Attribute und Methoden müssen public oder protected sein, sind sie auf private gesetzt, so kann eine Unterklasse nicht darauf zugreifen). In den konkreten Möbelklassen wie Tisch und Stuhl selbst stehen nur noch die Attribute und Methoden, die nur die spezielle Klasse selbst benötigt. Kü /Info Oberstufe OOP - Raumplanung Sj. 2012/2013 abstract Beschreibt die Oberklasse so wie hier selbst kein konkretes Objekt, sondern ist nur ein „abstrakter“ Sammel-Eimer, so wird die Oberklasse auch als abstract gekennzeichnet. Man verhindert dadurch, dass von ihr konkrete Objekte gebildet werden können. In unserem Fall ist das auch sinnvoll, denn sie dient nur dem Sammeln von Attributen und Methoden, aber ein allgemeines „Moebel-Objekt kann die Leinwand gar nicht verarbeiten, es darf also auch nicht erzeugt werden. Wichtig: eine Oberklasse, die als „abstract“ gekennzeichnet ist, muss mindestens eine abstrakte Methode haben – d.h. eine Methode, die nicht in dieser Klasse selbst implementiert wird, dafür aber durch ihre Kennzeichnung als „abstract“ erzwingt, dass jede Unterklasse sie implementier (siehe Kapitel Probleme…)t. Bsp.: public abstract void test(); <-ohne geschweifte Klammer, nur die Signatur und dann ein Semikolon protected/public Gegenüber der Methode gibAktuelleFigur() der ursprünglichen Klassendefinition hat sich außerdem die Angabe zur Sichtbarkeit geändert. Weshalb das notwendig ist, kann man leicht feststellen, wenn man es bei private belässt: Bei dieser Definition kann die Methode, die nun in einer anderen Klasse steht, nicht aufgerufen werden. protected macht eine Methode innerhalb des gesamten Paketes sichtbar, nicht aber nach außen (dafür müsste es auf public gesetzt sein). extends Es ergibt sich das Problem, dass unser Projekt nun zwar eine neue Klasse hat, BlueJ und JAVA aber nichts davon wissen, dass die Möbelklassen ihre Attribute und Methoden von der Klasse Moebel erben sollen. Das müssen wir in den Klassentext einarbeiten. Der Kopf wird ergänzt um extends Moebel . Nun weiß JAVA beim Übersetzen, dass es dort nach den fehlenden Attributen und Methoden nachsehen kann. Probleme, die entstehen, wenn man mit einer Oberklasse Moebel arbeitet Wenn wir die gemeinsamen Methoden in die neue Klasse Moebel ausgegliedert haben und die verkürzte Methode gibAktuelleFigur() in der konkreten Möbelklasse – z.B. Stuhl – verblieben ist, dann kennt die Klasse Moebel die Methode gibAktuelleFigur() nicht. Es gibt aber Methoden in Moebel, die darauf zugreifen! Das muss zu einem Fehler führen. Wir können das auf zwei Arten lösen: 1. Wir erstellen eine eigene Methode gibAktuelleFigur() in Moebel, die nichts tut (z.B. leer). In diesem Fall wird die Methode von Moebel durch die erbende Klasse überschrieben. 2. Wir erstellen einen reinen Methodenkopf und fügen ihm das Wort abstract hinzu. In diesem Fall wird unsere Klasse aber notwendig eine abstrakte Klasse, also eine Klasse, von der keine Objekte erzeugt werden können. Viele andere Beispiele für die unter 2. beschriebene Variante finden wir in den von uns verwendeten Grafikklassen, Kü /Info Oberstufe OOP - Raumplanung Sj. 2012/2013 z.B. Ellipse2D, von der selbst keine Objekte erzeugt werden, sondern nur von Ellipse2D.Double und Ellipse2D.Float Merke: In einer Klasse, die als abstract definiert ist, können Methoden ebenfalls als abstract definiert werden. In diesem Falle braucht man für die Methode keine konkrete Implementierung angeben, es reicht der Methodenkopf. Die konkrete Implementierung muss dann aber in der Unterklassen erfolgen. Beachte: die Methodensuche fängt immer in der Klasse des gerade betrachteten Objektes an und arbeitet sich dann von Oberklasse zu Oberklasse nach oben hin vor, bis sie eine Methode gefunden hat, die passt. Findet man auf dem Vererbungsweg keine passende Methode (also sowohl in der Klasse, in der sie aufgerufen wurde, also auch in einer der Oberklassen), so gibt es eine Fehlermeldung. Die verbleibende Klassendefinition von Tisch ist dann nur noch sehr kurz: import java.awt.Shape; import java.awt.geom.Ellipse2D; /** * Ein Tisch, der manipuliert werden kann und sich ... */ public class Tisch extends Moebel { public Tisch() { xPosition = 120; yPosition = 150; orientierung = 0; farbe = "rot"; istSichtbar = false; breite = 120; tiefe = 100; } /** * Berechnet das zu zeichnende Shape anhand der gegebenen Daten */ protected Shape gibAktuelleFigur() { Shape tisch = new Ellipse2D.Double(0 , 0, breite, tiefe); Rectangle2D umriss = tisch.getBounds2D(); // nun noch transformieren: AffineTransform t = new AffineTransform(); t.translate(xPosition, yPosition); t.rotate(Math.toRadians(orientierung), umriss.getX()+umriss.getWidth()/2, umriss.getY()+umriss.getHeight()/2); return t.createTransformedShape(tisch); } }