Polymorphie
Sind zwei Ausdrücke vom Aussehen her teilweise oder im Ganzen identisch, können sie unter Umständen dennoch - abhängig vom jeweiligen Kontext - etwas völlig Unterschiedliches bedeuten. Das Konzept, das dieses ermöglicht, nennt sich Polymorphie (oder auch: Polymorphismus) und gehört (neben Abstraktion, Kapselung und Vererbung) zu den wesentlichen Merkmalen objektorientierter Programmierung.
Sind etwa im Ausdruck a + b a und b Variablen vom Typ int, so werden während der Auswertung dieses Ausdrucks die Werte von a bzw. b addiert und das Rechenergebnis wird als integer-Wert zurückgeliefert; sind a und b float-Variablen, so werden während der Auswertung dieses Ausdrucks die Werte von a bzw. b addiert und das Rechenergebnis wird als float-Wert zurückgeliefert (die Addition von integer-Werten und die Addition von Fließkommawerten werden höchst unterschiedlich bewerkstelligt!). Sind im Ausdruck a + b a und b Zeichenketten, so ergibt a + b eine neue Zeichenkette durch Konkatenation von a und b, das heißt, die Inhalte von a und b werden aneinander gehängt. Der binäre Operator „+“ ist überladen, hat also je nach Typ der Operanden eine ganz verschiedene Bedeutung.
Der Plus-Operator ist in Java der einzige überladene Operator. Darüberhinaus ist es in Java nicht möglich, Operatoren zu überladen. Dies ist bei Methoden anders:
Man kann in einer Klasse immer eine neue Methode implementieren, die denselben Namen trägt wie eine in der Superklasse oder in der Klasse selbst bereits vorhandene Methode, sofern sich beide Methoden in ihrer Signatur (Zahl und Typen der Parameter) voneinander unterscheiden. Mit anderen Worten: Es ist möglich, Methoden nicht nur innerhalb einer Klasse zu überladen, sondern auch über Klassengrenzen hinweg. Ein Beispiel:
// D.java
class A {
int add(int a, int b) {
return a + b;
}
}
class B extends A {
int add(int a, int b, int c) {
return a + b + c;
}
}
class C extends B {
int add(int a) {
return a;
}
}
class D extends C {
D() {
System.out.println(add(3, 4));
System.out.println(add(2));
System.out.println(add(3, 4, 5));
}
public static void main(String[] args) {
new D();
}
}
Dieses Programm liefert folgende Ausgabe:
7
2
12
Man bedenke, dass aufgrund der sukzessiven Vererbung zur durch new D(); erzeugten Instanz der Klasse D sowohl die Methode add der Klasse C, als auch die Methode add der Klasse B und die Methode add der Klasse A gehören.
Wird in einer Klasse B eine von der Superklasse A geerbte (und in A weder mit private, noch mit static oder final deklarierten) Methode doXYZ überschrieben, so überdeckt die polymorphe Methode doXYZ eines Objektes vom Typ B die in der Klasse A implementierte Methode doXYZ. Ein Beispiel:
// TestPrg.java
class A {
void output() {
System.out.println("A");
}
}
class B extends A {
@Override
void output() {
System.out.println("B");
}
}
class TestPrg {
public static void main(String[] args) {
A objB = new B();
boolean isOfTypA = objB instanceof A;
boolean isOfTypB = objB instanceof B;
System.out.println("objB is of Typ A: "+ isOfTypA);
System.out.println("objB is of Typ B: "+ isOfTypB);
objB.output();
A objA = new A();
isOfTypA = objA instanceof A;
isOfTypB = objA instanceof B;
System.out.println("objB is of Typ A: "+ isOfTypA);
System.out.println("objB is of Typ B: "+ isOfTypB);
objA.output();
}
}
TestPrg liefert folgende Ausgabe:
objB is of Typ A: true
objB is of Typ B: true
B
objB is of Typ A: true
objB is of Typ B: false
A
Polymorphe Methoden werden dynamisch gebunden, das heißt, dass erst zur Laufzeit eines Programms anhand des Typs des die Methode aufrufenden Objekts die Entscheidung getroffen wird, welche der unterschiedlichen Implementierungen „genommen“ wird.