Home > Informatik > Stufe Q1

Mehrfachvererbung

Allgemeines - Klassendiagramme - Mehrfachvererbung

Problemstellung

In Programmiersprachen wie C++ gibt es eine sogenannte Mehrfachvererbung. Eine Klasse kann von zwei oder mehr Mutterklassen gleichzeitig Attribute und Methoden erben. In Java ist das so nicht möglich - jedenfalls nicht direkt. Es gibt jedoch einen "Workaround", mit dem man doch eine Art Mehrfachvererbung realisieren kann. Dieser Workaround nennt sich Interface-Technik.

Interfaces

Ein Interface ist eine Java-Klasse, die keine ausformulierten Methoden zur Verfügung stellt, sondern nur die Signaturen von Methoden. Eine Methoden-Signatur ist nichts anderes als der "Kopf" einer Methode, die erste Zeile sozusagen.

Eine Methode:
    public void aufnehmen(Gegenstand neu)
    {
       if (anzahlDinge < 12)
          dinge[anzahlDinge++] = neu;
    }
Eine Signatur:
    public void aufnehmen(Gegenstand neu);

Eine Signatur enthält also den kompletten Methoden-Kopf einschließlich aller Parameter, gefolgt von einem Semikolon.

Ein Beispiel

Betrachten wir das Interface Comparable aus dem Programmierprojekt "Binärer Suchbaum für Objekte".

public interface Comparable
{
   public boolean isLess(Comparable c);
   public boolean isEqual(Comparable c);
   public boolean isGreater(Comparable c);
   public double  getValue();
}

Im Grunde macht das Interface überhaupt nichts. Es schreibt lediglich vor, welche Methoden eine Klasse, die dieses Interface einbindet, selbst implementieren muss. Sie alle kennen bestimmt das Problem, wenn Sie in einem Java-Applet das Interface ActionListener benutzen. Beim ersten Kompilieren des Quelltextes "meckert" der Compiler dann, dass wir bitte die Methode ActionPerformed implementieren sollen. Das Interface schreibt nämlich vor, dass das aufrufende Applet genau diese Methode selbst implementieren muss. Tut sie das nicht, gibt es eine Fehlermeldung.

Die Klasse Item - wieder aus dem Programmierprojekt "Binärer Suchbaum für Objekte" - implementiert nun das Interface Comparable, außerdem das Interface Printable.

public abstract class Item implements Comparable, Printable
{  
    public abstract double getValue();

    public boolean isLess(Comparable c)
    {
       return c.getValue() < getValue();
    }
    
    public boolean isEqual(Comparable c)
    {
       return c.getValue() == getValue();
    }    
    
    public boolean isGreater(Comparable c)
    {
       return c.getValue() > getValue();
    }      
    
    public abstract void println();
    
    public abstract void print();
}

Die Klasse Item ist eine abstrakte Mutterklasse für später zu implementierende Tochterklassen. Da Item noch nicht weiß, wie die späteren Tochterklassen ihren "Wert" berechnen, implementiert Item zunächst eine abstrakte default-Methode für getValue. Eine Tochterklasse von Item muss diese Methode dann überschreiben, sonst gibt es eine Fehlermeldung des Compilers.

Die Methoden isLess etc. können allerdings schon implementiert werden, denn zur Laufzeit des Programms werden die getValue-Methoden der späteren Tochterklassen aufgerufen, die dann ja - hoffentlich - korrekte Werte liefern.

Die beiden Ausgabe-Methoden, die von Printable "geerbt" wurden, bleiben zunächst abstrakt. Auch hier gilt wieder, dass Item "keine Ahnung" davon hat, was die späteren Tochterklassen einmal ausgeben wollen. Es ist die Aufgabe der Tochterklassen, die beiden Ausgabe-Methoden zu überschreiben. Durch das Schlüsselwort abstract werden die Tochterklasse dazu gezwungen.

Schauen wir uns noch kurz eine Tochterklasse von Item an, um das Ganze abzuschließen.

public class Bruch extends Item
{
    int zaehler, nenner;
    
    public Bruch(int pZaehler, int pNenner)
    {
        zaehler = pZaehler;
        nenner  = pNenner;        
    }

    public double getValue()
    {
       double z,n;
       z = (double) zaehler * 1.0;
       n = (double) nenner * 1.0;
       return z/n;
    }
    
    public void println()
    {
        System.out.println(zaehler+"/"+nenner+"\t = "+getValue());
    }
    
    public void print()
    {
        System.out.print(zaehler+"/"+nenner+"\t");
    }
}

Die Comparable-Methoden sind hier rot hervorgehoben, die Printable-Methoden grün. Achten Sie darauf, dass in der Signatur der Klasse nur "extends Item" steht, nicht aber "implements Comparable, Printable". Das ist nicht nötig, weil die Klasse Bruch eine Tochterklasse von Item ist, und in der Klasse Item wurde bereits für die Einbindung von Comparable und Printable gesorgt.

Hier das BlueJ-Klassendiagramm:

Das BlueJ-Klassendiagramm mit den  Klassen Comparable, Printable, Item, Bruch und Bruchtest.

Das BlueJ-Klassendiagramm

Interessant ist die Beziehung zwischen Item und Comparable. Einerseits besteht hier eine IST-Beziehung, denn Item ist eine Tochterklasse von Comparable. Andererseits besteht eine KENNT-Beziehung, denn Item benutzt einen Parameter der Comparable Comparable in seinen Methoden. Eigentlich müsste man von Item zu Comparable noch einen zweiten Pfeil zeichnen, der diese KENNT-Beziehung kennzeichnet.

Weiterführende Literatur:

Mehrfachvererbung in Lahrens, Rayman, "Objektorientierte Programmierung", 2. Auflage 2009, Rheinwerk Computing.

Vererbung und Polymorphismus in Herold, Lurz, Wohlrab, Hopf, "Grundlagen der Informatik", 2017, Pearson-Verlag.