Weiter zum Inhalt

04.05 Vererbung

Instanceof und Cast

Wir ändern nun das vorherige Beispiel etwas ab. Wir belassen alle drei Klassen Person, Manager und Programmierer so wie sie sind und ändern nur die main Methode.

public static void main(String[] args) {

  Person personen[] = new Person[3];
  String s = "";

  personen[0] = new Person("Horst");
  personen[1] = new Programmierer("Sebastian", "Java");
  personen[2] = new Manager("Wendelin", 50000000);

  for (int i = 0; i < personen.length; i++) {

    s = personen[i].getName();
    if (personen[i] instanceof Manager) {
      s += " " + ((Manager)personen[i]).getGehalt();
    }
    else if (personen[i] instanceof Programmierer) {
      s += "  " + ((Programmierer)personen[i]).getLieblingsSprache();
    }

    System.out.println(s);
  }
}

An Stelle der drei einzelnen Objekte möchten wir nun alle Objekte in ein Feld verpacken. Dazu deklarieren wir ein Array personen, welches Elemente von Typ Person aufnehmen kann.
Das erste Element ist wieder unser “Horst”. Als zweites Element soll nun ein Objekt der Klasse Programmierer eingefügt werden. Das Array kann doch aber nur Elemente des Typs Person enthalten, werden Sie sich jetzt sicher fragen. Hierzu soll noch einmal folgendes wiederholt werden. Da die Klasse Programmierer eine Kindklasse von Person ist, gilt wie bereits erwähnt: Jeder Programmierer ist eine Person. Somit ist es auch kein Problem ein Objekt der Klasse Programmierer in dieses Array einzufügen. Gleiches gilt auch wieder für unseren Manager “Wendelin”. Nun möchten wir aber wieder so eine schöne Ausgabe haben wie in unserem ersten Beispiel zur Vererbung. Das gestaltet sich nun nicht mehr ganz so einfach wie zuvor. Wir haben jetzt ein Feld, dessen Elemente alle Objekte der Klasse Person sind. Wenn wir jetzt schreiben würden personen[i].getGehalt() würde dies nicht funktionieren. Die Klasse Person kennt nämlich keine Methode namens getGehalt. Diese ist ja nur in unserer Klasse Manager implementiert. Es muss also nun eine Möglichkeit geben, die im Array enthaltenen Objekte dahin gehend zu überprüfen, zu welcher Klasse sie denn gehören. Für diesen Zweck gibt es in Java den instanceof Operator.

if (meinObjekt instanceof MeineKlasse) {}

Hiermit kann man überprüfen, ob meinObjekt eine Instanz von MeineKlasse ist. In unserem Fall überprüfen wir also die Elemente des Arrays dahingehend, ob es Instanzen der Klasse Manager bzw. Programmierer sind. Damit haben wir aber erst einmal nur die Hälfte geschafft.
Wir wissen zwar nun, dass beispielsweise unser aktuelles Feldelement ein Objekt der Klasse Manager ist, aber dennoch ist jedes Element des Arrays immer noch vom Typ Person. Es muss nun eine Möglichkeit geben ein Objekt, von dem wir nun wissen von welcher Klasse es ist, auch wieder in Objekt dieser Klasse umzuwandeln. Dies ist in Java recht einfach möglich.

(KonkreteKlasse)objekt

Man schreibt einfach vor das Objekt in runden Klammern die Klasse, in welche wir das dahinter stehende Objekt gerne umwandeln möchten. Man spricht hierbei von einem Cast (eng. Abguss). In unserem Fall bedeutet dies, Objekte der Klasse Person müssen in Objekte der Klassen Manager bzw. Programmierer umgewandelt werden. Dies geschieht durch ((Manager)personen[i]) bzw.((Programmierer)personen[i]). Innerhalb der Klammern wandeln wir nun ein Objekt vom Typ Person in das entsprechende konkretere Objekt um. Somit können wir dann auch die jeweiligen Methoden der konkreten Klassen, wie getGehalt und getLieblingsSprache, verwenden. Als Ausgabe erhalten wir.

Horst
Sebastian  Java
Wendelin 50000000

Vererbung und static

Interessant ist noch das Verhalten statischer Elemente bei der Vererbung. Hier kann es häufig zu Fehlern kommen, da man mitunter die Wirkungsweise bzw. die interne Abarbeitung nicht berücksichtigt. Hierzu ein kleines Beispiel.

public class Vater {

  protected static int classAttr = 0;
  protected int objectAttr = 0;

  public static void setClassAttr(int aNewValue) {

    Vater.classAttr = aNewValue;
  }

  public static int getClassAttr() {

    return Vater.classAttr;
  }

  public void setObjectAttr(int aNewValue) {

    this.objectAttr = aNewValue;
  }

  public int getObjectAttr() {

    return this.objectAttr;
  }
}

Dies ist unsere Klasse von der später eine andere Klasse erben soll. Sie enthält jeweils ein Objekt- und ein Klassenattribut, welche mit den zugehörigen Set- bzw. Get-Methoden gesetzt bzw. abgefragt werden können. Nun lassen wir eine Klasse Kind von der Klasse Vater erben.

public class Kind extends Vater {

  public static void setClassAttr(int aNewValue) {

    Kind.classAttr = aNewValue;
  }

  public static int getClassAttr() {

    return Kind.classAttr;
  }

  public void setObjectAttr(int aNewValue) {

    this.objectAttr = aNewValue;
  }

  public int getObjectAttr() {

    return this.objectAttr;
  }
}

Nun testen wir das Verhalten beider Klassen.

public static void main(String[] args) {

  Vater.setClassAttr(5);
  Kind.setClassAttr(3);

  Vater vater = new Vater();
  vater.setObjectAttr(6);
  Kind kind = new Kind();
  kind.setObjectAttr(4);

  System.out.println(Vater.getClassAttr());
  System.out.println(Kind.getClassAttr());

  System.out.println(vater.getObjectAttr());
  System.out.println(kind.getObjectAttr());
}

Wir erhalten überraschend folgende Ausgabe:

3
3
6
4

Für die Ausgabe der Klassenattribute erhalten wir für Kind und Vater jeweils 3 und für die Objektattribute 4 und 6. Dies hat folgenden Hintergrund. Das Klassenattribut classAttr existiert unabhängig von einer konkreten Instanz der Klasse Vater bzw. eines Erben davon. Es ist somit gesehen auch nur einmal vorhanden. Innerhalb der Methode public static void setClassAttr(int aNewValue) der Klasse Kind steht zwar Kind.classAttr = aNewValue; dennoch ändert diese Zuweisung das Klassenattribut der Vaterklasse. Somit wird das Attribut classAttr der Klasse Vater innerhalb der Main-Methode erst auf 5 (Vater.setClassAttr(5);) und danach sofort auf 3 (Kind.setClassAttr(3);) gesetzt. Anders verhält es sich bei dem Objektattribut objectAttr. Dieses Attribut ist immer an eine konkrete Instanz gebunden und existiert somit auch für jede einzelne Instanz. Die Methoden public void setObjectAttr(int aNewValue) und public int getObjectAttr() der Klasse Vater bzw. Kind arbeiten demnach auch immer mit dem zur Instanz zugehörigen Attribut. Als Ausgabe erhalten wir deswegen auch richtig 6 und 4.

Man kann noch hinzufügen, dass man in der Klasse Kind auch alle Methoden weglassen könnte. Die statischen Methoden arbeiten mit dem Klassenattribut classAttr der Vaterklasse und die Objektmethoden überschreiben auch nur die Methoden der Vaterklasse bei Verwendung der gleichen Logik. Somit würde ein

public class Kind extends Vater {}

ebenfalls zum gleichen Ergebnis führen.

Im Kapitel 04.06. Polymorphie erfahren Sie mehr dazu, wie man vererbte Methoden überschreiben kann, um ein vielgestaltiges Verhalten zu realisieren.


Kommentar verfassen

Dein E-Mail wird nicht veröffentlicht oder weitergegeben. Pflichtfelder sind mit * markiert.