MezData-Logo

Polymorphie

Polymorphie bedeutet Vielgestaltigkeit. Oder anders –das folgende Kapitel ist ungefähr so schwierig wie mit einem Betonmischer Kuchenteig zu rühren.

Statische Polymorphie (Überladen von Methoden)

Deklariert man in einer Klasse mehrere Methoden mit dem selben Namen aber unterschiedlichen Parameterlisten spricht man vom Überladen der Methode. Der Compiler kann die jeweils passende Methode anhand der Signatur des Aufrufs ermitteln -name + Typ-Reihenfolge der Parameter, z.B. plus(GZ,GZ). Weil der Compiler bereits zur Übersetzungszeit ermitteln kann welches Unterprogramm aufgerufen werden soll spricht man hier von Statischer Polymorphie.

Klasse ArbeitsUmgebung Klasse A
public class ArbeitsUmgebung{
  public static void main (String[] args){     // Startfunktion 
    System.out.println("Willkommen bei der Arbeit!");
    System.out.println("A einA = new A();");
    A einA = new A();
    System.out.print("einA.ma(5);   ");
    einA.ma(5);
    System.out.print("einA.ma(4.5); ");
    einA.ma(4.5);
  }  
}
public class A{
  public void ma(int n){
    System.out.println("// A.ma(GZ)");
  }
  public void ma(double n){
    System.out.println("// A.ma(FKZ)");
  }
}
Willkommen bei der Arbeit!
A einA = new A();
einA.ma(5);   // A.ma(GZ)
einA.ma(4.5); // A.ma(FKZ)

Überschreiben von Methoden bei Vererbung (Dynamische Polymorphie, dynamisches Binden)

Die Methode ma(GZ) von B überschreibt die geerbte Methode ma(GZ) von A:

public class B extends A{
  public void ma(int n){
    System.out.println("// B.ma(GZ)");
  } 
}
Überschreiben von Methoden
A einA = new A();
einA.ma(5);   // A.ma(GZ)
B einB = new B();
einB.ma(5);   // B.ma(GZ)
einB.ma(4.5); // A.ma(FKZ)
einA = new B();
einA.ma(5); // B.ma(GZ)
System.out.println("A einA = new A();");
A einA = new A();
System.out.print("einA.ma(5); ");
einA.ma(5);
System.out.println("B einB = new B();");
B einB = new B();
System.out.print("einB.ma(5); ");
einB.ma(5);
System.out.print("einB.ma(4.5); ");
einB.ma(4.5);
System.out.println("einA = new B();");
einA = new B();
System.out.print("einA.ma(5); ");
einA.ma(5);

Geht man nun der Frage nach ob der Compiler bereits zur Compile-Zeit entscheiden kann welche Methode aufgerufen wird, erkennt man die notwendigen dynamischen Prozesse zur Laufzeit des Programms: Beim Aufruf von einA.ma(5); kann erst anhand des konkreten Objekts zur Laufzeit entschieden werden welche Methode A.ma(5) bzw. B.ma(5) gemeint ist.

Alles super

Mit Hilfe des Schlüsselworts super kann in einer Sub-Klasse auf eine Methode mit gleicher Signatur aus der Super-Klasse zugegriffen werden.

public class B extends A{
  public void ma(int n){
    super.ma(n);
    System.out.println("// B.ma(GZ)");
  } 
}
Alles Super
B einB = new B();
einB.ma(5);   // A.ma(GZ)
// B.ma(GZ)
public static void main (String[] args){
System.out.println("Alles Super");
System.out.println("B einB = new B();");
B einB = new B();
System.out.print("einB.ma(5); ");
einB.ma(5);
}

Zuweisungskompatibilität, Up-Cast

Ein B ist auch ein A, kann alles was A kann. Aber ein A ist noch lange kein B! Somit kann man A einA = new B(); zuweisen aber nicht B einB= new A();

Down-Cast

Wie kann ich den Compiler überreden mir auch die speziellen B-Methoden zu erlauben wenn ich nur einen A-Verweis zu einem B-Objekt habe –verstanden ;-)?

public class B extends A{
  public void ma(int n){
    System.out.println("// B.ma(GZ)");
  }
  public void mb(int n){
    System.out.println("// B.mb(GZ)");
  }
}
public static void main (String[] args){
  System.out.println("Down-Cast");
  System.out.println("A einA = new B();");
  A einA = new B();
  System.out.print("((B)einA).mb(5); ");
  ((B)einA).mb(5); // Type Cast
 }  

Ohne Type Cast findet der Compiler B.mb(GZ) nicht

Down-Cast
A einA = new B();
((B)einA).mb(5); // B.mb(GZ)

Abstrakte Klassen

Die Klassen B und C haben viel gemeinsam und sollen wie eine Klasse in der Arbeitsumgebung verwaltet werden. Dazu generalisiert man die Gemeinsamkeiten in einer SuperKlasse A. Die jeweiligen Methoden ma(GZ) der Klassen sind unterschiedlich und können in A nicht realisert werden. Eine Möglichkeit wäre mittels Down-Cast das Problem zu lösen. Der bessere Weg ist jedoch in A die Methode ma(GZ) bereits zu deklarieren, da keine Definition erfolgt wird die Methode als abstract gekennzeichnet. Somit existiert eine abstrakte Methode in der Klasse, daraus folgt dass die Klasse A abstrakt ist, es kann deshalb keine Instanz (Objekt) von A gebildet werden. Ein Verweis vom Typ A kann nun Objekte vom Typ B und C aufnehmen und dem Compiler ist bekannt das in den Objekten eine Methode ma(GZ) definiert ist.

public abstract class A{
	 public abstract void ma(int n);
}
Abstrakte Klassen
A einA;
einA = new B();
einA.ma(5);   // B.ma(GZ)
einA = new C();
einA.ma(5);   // C.ma(GZ)
System.out.println("Abstrakte Klassen");
System.out.println("A einA;");
A einA;
System.out.println("einA = new B();");
einA = new B();
System.out.print("einA.ma(5);   ");
einA.ma(5);
System.out.println("einA = new C();");
einA = new C();
System.out.print("einA.ma(5);   ");
einA.ma(5);

Sandkasten: polymorphie.zip

Trainingslager

1. Übung

public abstract class A{
  private static int idcount = 0;
  protected int id;
  public A(){
    id = ++idcount;
  }    
  public abstract void show();
}
public class B extends A{
  public void show(){
    System.out.println("// B.show() "+id);
  } 
}
public class C extends A{
  public void show(){
    System.out.println("// C.show() "+id);
  } 
}

1.1 Erstelle ein Klassendiagramm. Lösung: 1uebung-klass.png 1uebung.jude

public class ArbeitsUmgebung{
  public static void main (String[] args){     // Startfunktion 
    System.out.println("Trainingslager");
    System.out.println("A einA;");
    A einA;
    System.out.println("einA = new B();");
    einA = new B();
    System.out.print("einA.show();   ");
    einA.show();
    System.out.println("einA = new C();");
    einA = new C();
    System.out.print("einA.show();   ");
    einA.show();
  }
}

1.2 Welche Ausgabe wird erzeugt? Lösung: polymorphie2.zip

1.3 Erstelle Sequenzdiagramm für ArbeitsUmgebung.main(..)