Home > Informatik > Informatik EF > Folge 6

Folge 6.1 - Warum Vererbung?

Vererbung - Oberklassen - Polymorphie

In der Folge 5.1 haben wir Java-Anwendungen kennengelernt. Eine KI hat uns das Grundgerüst für solche Anwendungen erstellt, und wir haben anschließend nur die wichtige Methode paintComponent() verändert und dort Kreise, Rechtecke und Linien gezeichnet. Dabei haben wir außerdem for- und while-Schleifen eingesetzt, um dieses wichtige Thema zu vertiefen.

In dieser Folge lernen wir nun ein weiteres wichtiges Konzept der Objektorientierten Programmierung (OOP) kennen, nämlich die Vererbung.

Lernziele

Wenn Sie diese Seite und die beiden Folgeseiten durchgearbeitet haben, sollten Sie

  • erklären können, warum man in der objektorientierten Programmierung Vererbung einsetzt.
  • den Unterschied zwischen einer Oberklasse und einer Unterklasse erklären können.
  • eine einfache Oberklasse entwickeln können, welche gemeinsame Attribute und Methoden mehrerer Klassen zusammenfasst.
  • mit dem Schlüsselwort extends eine Unterklasse von einer Oberklasse ableiten können.
  • den Konstruktor einer Oberklasse mit super(...) aufrufen können.
  • erklären können, welche Vorteile Vererbung gegenüber dem Kopieren von Quelltext bietet.
  • den Unterschied zwischen einer konkreten und einer abstrakten Klasse kennen.
  • erklären können, wozu abstrakte Methoden dienen.
  • Methoden einer Oberklasse in einer Unterklasse überschreiben können.
  • das Grundprinzip der Polymorphie anhand einfacher Beispiele erläutern können.

Implementierung der Klasse Rectangle

Schauen wir uns noch einmal die Übung 5.1-8 aus der Folge 5.1 an:

Übung 5.1-8

Erstellen Sie eine Klasse Rectangle, mit der Rechtecke und Quadrate erzeugt werden können. Auch hier soll die Randfarbe und die Randstärke durch Setter-Methoden festgelegt werden.

Hier sollten Sie eine Klasse Rectangle implementieren, mit der man farbige Rechtecke zeichnen kann. Vor dieser Übung hatten Sie bereits die Klasse Circle implementiert, mit der man farbige Kreise zeichnen konnte. Bei der Erstellung der Klasse Rectangle sind Sie dann wahrscheinlich so vorgegangen, wie die meisten Programmierer das machen:

Typisches Vorgehen

  1. Man kopiert den Quelltext der Klasse Circle in die Zwischenablage
  2. Man erstellt eine neue Klasse Rectangle
  3. Man kopiert den Inhalt der Zwischenablage in das leere Editorfenster
  4. Man verändert den Circle-Quelltext so lange, bis daraus eine funktionierende Klasse Rectangle entstanden ist.

Bei der Erstellung der Klasse Rectangle fällt auf, dass am ursprünglichen Quelltext der Klasse Circle gar nicht so viele Änderungen vorgenommen werden müssen.

Vergleich der Quelltexte der beiden Klassen Circle und Rectangle
Autor: Ulrich Helmich 2026, Lizenz: Public Domain

Dieses Bild zeigt sehr eindrucksvoll, dass die Quelltexte der beiden Klassen Circle und Rectangle zu ca. 90% übereinsimmen. Schauen wir uns das etwas genauer an:

Instanzvariablen

  • Die Instanzvariablen xPos und yPos können wir unverändert übernehmen
  • Die Instanzvariable radius muss durch die neuen Instanzvariablen breite und hoehe ersetzt werden.

Konstruktor

Der Konstruktor der Klasse Circle muss an einigen Stellen angepasst werden. Für Rectangle-Objekte benötigen wir jetzt vier Parameter, zwei für die Position und zwei für Breite und Höhe.

Circle-Konstruktor
    public Circle(int xPos, int yPos, int radius)
    {
        this.xPos = xPos;
        this.yPos = yPos;
        this.radius = radius;

        color = Color.RED;
        randfarbe = Color.BLACK;
        randstaerke = 1;
    }
Rectangle-Konstruktor
   public Rectangle(int xPos, int yPos, int breite, int hoehe) // neu
    {
        this.xPos = xPos;
        this.yPos = yPos;
        this.breite = breite; // neu
        this.hoehe  = hoehe;  // neu

        color = Color.RED;
        randfarbe = Color.BLACK;
        randstaerke = 1;
    }

Abgesehen von diesen wenigen Änderungen sind die beiden Konstruktoren nahezu identisch.

Methoden

Circle-Methoden
    public void setColor(Color color)
    {
        this.color = color;
    }

    public void setRandfarbe(Color randfarbe)
    {
        this.randfarbe = randfarbe;
    }

    public void setRandstaerke(int randstaerke)
    {
        if (randstaerke < 1)
            randstaerke = 1;

        this.randstaerke = randstaerke;
    }
Rectangle-Methoden
    public void setColor(Color color)
    {
        this.color = color;
    }

    public void setRandfarbe(Color randfarbe)
    {
        this.randfarbe = randfarbe;
    }

    public void setRandstaerke(int randstaerke)
    {
        if (randstaerke < 1)
            randstaerke = 1;

        this.randstaerke = randstaerke;
    }

Die drei Methoden sind absolut identisch, hier musste überhaupt nicht geändert werden.

Circle.paint()
    public void paint(Graphics g)
    {
       g.setColor(randfarbe);
       g.fillOval(xPos-radius,yPos-radius,radius*2,radius*2);
       g.setColor(color);
       g.fillOval(xPos-radius+randstaerke,
                  yPos-radius+randstaerke,
                  radius*2-2*randstaerke,
                  radius*2-2*randstaerke);
    }
Rectangle.paint()
   public void paint(Graphics g)
    {
        g.setColor(randfarbe);
        g.fillRect(xPos,yPos,breite,hoehe);
        g.setColor(color);
        g.fillRect(xPos+randstaerke,yPos+randstaerke,
                   breite-2*randstaerke,
                   hoehe-2*randstaerke);        
   }

Auch diese beiden Methoden besitzen denselben grundsätzlichen Aufbau. Unterschiede gibt es jedoch bei den Koordinaten und den Zeichenbefehlen. Beim Kreis geben die Koordinaten den Mittelpunkt an, beim Rechteck dagegen die linke obere Ecke. Außerdem wird beim Kreis der Radius verwendet, während beim Rechteck Breite und Höhe angegeben werden.

Beurteilung dieses Verfahrens

Zunächst einmal muss man feststellen, dass dieses Verfahren durchaus funktioniert. Viele - auch professionelle - Programmierer und die leider viel zu wenigen Programmiererinnen machen das auch genau so.

Bei kleinen Projekten, wie sie im Informatikunterricht üblich sind, ist das auch zunächst kein Problem. Bei größeren Projekten mit vielen Tausend Zeilen Quelltext wäre es jedoch keine gute Idee, auf diese Weise neue Klassen zu erstellen. Die beiden Klassen Circle und Rectangle kann man sich noch nebeneinander auf einem Bildschirm ansehen und vergleichen. Wenn dann zusätzliche Funktionen implementiert werden sollen, beispielsweise verschiedene Randtypen (durchgehend, gestrichelt, gepunktet) oder Schlagschatten bei den Graphikobjekten, dann kann man diese Features bei beiden Quelltexten parallel programmieren. Circle im linken Fenster, Rectangle im rechten Fenster.

Wenn die Klassen aber 200, 500, 1000 oder noch mehr Zeilen umfassen, ist ein solches Vorgehen nicht mehr praktikabel. Man muss ständig hoch und runter scrollen und dabei übersieht man schon einmal eine Stelle - entweder in der einen Klasse oder in der anderen.

Wenn man dann glaubt, fertig zu sein, tauchen oft gravierende Fehler auf, weil man an der einen oder anderen Stelle vergessen hat, den Quelltext zu ändern. Bei langen Programmen kann dann eine solche Fehlersuche sehr lange dauern und auch zu Frustrationen führen.

Mit Hilfe von ChatGPT & Co kann man solche Fehler heute natürlich leicht aufspüren, aber professionell ist ein solches Vorgehen nicht.

Das Konzept der Vererbung

Um solche redundanten, fehleranfälligen Quelltexte zu vermeiden, hat man das Konzept der Vererbung in die Programmierung eingeführt. Man erstellt eine Oberklasse mit allen wichtigen Funktionen, und leitet daraus zwei, drei oder mehr Unterklassen mit speziellen Aufgaben ab. Die Unterklassen erben dann die Variablen und Methoden der Oberklasse und müssen diese nicht mehr selbst implementieren.

In unserem Fall - geometrische Figuren - könnte das so aussehen:

Wir erstellen eine Klasse Figure und implementieren dort Instanzvariablen wie xPos, yPos, fillColor und so weiter sowie Methoden wie setFillColor(), setBorderColor()etc., die allgemeingültig sind, also für alle geometrischen Figuren gleich sind.

Dann erstellen wir eine Unterklasse Circle sowie eine Unterklasse Rectangle, die dann für die konkreten Graphik-Objekte zuständig sind und diese zeichnen können.

Auf dieses wichtige Konzept der Vererbung werden wir auf der nächsten Seite näher eingehen.