Wer sich mit Java beschäftigt sollte wissen was Polymorphie ist und wie sie funktioniert.
Hiermiet ergreife ich die Initiative euch die Polymorphie so gut wie möglich "beizubringen" (lol, das Wort ist ja mal so ein Jaein
================================================== ============
1. Polymorphie
1.1. Definition
Der Begriff Polymorphismus[1] kommt aus der griechischen Sprache. Diese Eigenschaft ist eine der Grundbegriffe[2] objektorientierter Programmierung.

1.2. Adhoc-Polymorphie
1.2.1. Typanpassung
Es kommt recht häufig vor, dass Datentypen konvertiert werden müssen. Java unterscheidet hier 2 Arten der Typanpassung:
1.2.1.1. Automatische oder implizite Typanpassung
Daten von einem kleineren Datentyp können ohne Datenverlust in einen größeren Datentyp umgewandelt werden.
Beispiele:
Code:
byte bValue=30; short sValue=bValue; int iValue=sValue+5; long lValue=11111111111111L; float fValue=lValue; double dValue=fValue;
Da es kein Byte-Literal gibt, sichert der Compiler, dass das Integerliteral
im Wertebereich [-128,127] bleibt.
Bei der impliziten Konvertierung von long in float oder double können
aufgrund der unterschiedlichen Speicherung Genauigkeitsfehler eintreten.
1.2.1.2. Explizite Typanpassung
Einer Variablen mit einem kleineren Datentyp kann ein Ausdruck eines größeren Typs mit möglichem Verlust von Informationen zugewiesen werden. Dies wird explizit mittels Casting erreicht.
Beispiele:
Code:
double dValue=1234.5E27; byte bValue=(byte) dValue; short sValue=(short) dValue; int iValue=(int) dValue+5; long lValue=11111111111111L; float fValue=(float) lValue;
1.2.2. Überladen
Eine Methode ist gekennzeichnet durch Rückgabewert, Name, Parameter und unter Umständen durch Ausnahmefehler, die sie auslösen kann. Ein wichtiger Begriff in diesem Zusammenhang ist die Signatur einer
Methode.
Die Signatur einer Methode ist der Name der Methode und die Typen in
ihrer Parameterliste:
Code:
public static void irgendeineMethode( int i, double d, String s){
}
Java erlaubt es, den Namen der Methode gleich zu lassen, aber andere Parameter einzusetzen. Eine überladene Methode ist eine Methode mit dem gleichen Namen wie eine andere Methode, unterscheidet sich aber durch eine unterschiedliche Parameterliste.
Das ist auf zwei Arten möglich:
- Eine Methode hat für den Compiler unterscheidbare Typen.
- Eine Methode akzeptiert eine unterschiedliche Anzahl von Parametern.
Code:
public static int max( int i, int j ) {
return Math.max( i, j );
}
public static int max( double d, int i){
return (int) Math.max(d, (double)i);
}
public static int max(int[] a){
int ret=a[0];
for(int i=1;i<a.length;i++)
if(a[i]>a[i-1]) ret=a[i];
return ret;
}
public static int max(int i,int j, int k){
return max( i, max(j, k) );
}
1.3. Universelle Polymorphie
1.3.1. Laufzeitpolymorphie
Beispiel:
Code:
public class A {
static void print(String s) { System.out.println(s); }
void p(char a) { print(""+'a'); }
void p(int a) { print("Aah!"); }
}
public class B extends A {
void p(int b) { // overrides second p
print("Beh!");
}
public static void main(String[] args) {
A a = new A();
a.p(1); // print „Aah!“
a = new B();
a.p(1); // print „Beh!“
}
}
Gewählt wird dynamisch die Methode des Objekttyps, nicht statisch die des Variablentyps. Man spricht daher auch von dynamischem Binden.
Die Methode p ist sozusagen „vielgestaltig“, polymorph. Diese Art der an die Vererbung (oder auch an die Implementierung einer Schnittstelle!) gebundene Polymorphie heißt auch Einschlusspolymorphie oder Inklusionspolymorphie.
1.3.1. Generizität
Die Generizität wird auch parametrisierte Polymorphie genannt. Ein und dieselbe Methode, Klasse, soll für Daten verschiedener Typen verwendbar sein.
Durch den Einsatz von formalen Typparametern wird diese Vielgestaltigkeit erreicht.
1.3.1.1. Generische Klassen
Aufgabenstellung String:
In einer Klasse soll ein Wert gespeichert werden, welcher über den Konstruktor übergeben wird. Dieser Wert kann ausgelesen bzw. mit einem Setter neugesetzt werden.
Dieser Wert soll vom Typ String sein:
Code:
public class WrapperString {
private String value;
public WrapperString(String t){
this.value=t;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString(){
return ""+getValue();
}
public static void main(String[] a){
WrapperString s=new WrapperString("1");
System.out.println(s.toString());
}
}
Aufgabenstellung Integer:
In einer Klasse soll ein Wert gespeichert werden, welcher über den Konstruktor übergeben wird. Dieser Wert kann ausgelesen bzw. mit einem Setter neugesetzt werden.
Dieser Wert soll vom Typ Integer sein:
Code:
public class WrapperInteger {
private Integer value;
public WrapperInteger(Integer t){
this.value=t;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
@Override
public String toString(){
return ""+getValue();
}
public static void main(String[] a){
WrapperInteger s=new WrapperInteger(2);
System.out.println(s.toString());
}
}
Aufgabenstellung Double:
In einer Klasse soll ein Wert gespeichert werden, welcher über den Konstruktor übergeben wird. Dieser Wert kann ausgelesen bzw. mit einem Setter neugesetzt werden.
Dieser Wert soll vom Typ Double sein:
Code:
public class WrapperDouble {
private Double value;
public WrapperDouble(Double t){
this.value=t;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
@Override
public String toString(){
return ""+getValue();
}
public static void main(String[] a){
WrapperDouble s=new WrapperDouble(3.6);
System.out.println(s.toString());
s.setValue(4.4);
System.out.println(s.toString());
}
}
Beispiel:
Code:
public class Wrapper<T> {
private T value;
public Wrapper(T t){
this.value=t;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
@Override
public String toString(){
return ""+getValue();
}
public static void main(String[] a){
Wrapper<String> s=new Wrapper<String>("1");
System.out.println(s.toString());
Wrapper<Integer> i=new Wrapper<>(2);
System.out.println(i.toString());
Wrapper<Double> d=new Wrapper<>(3.6);
System.out.println(d.toString());
d.setValue(4.4);
System.out.println(d.toString());
}
}
Seit Java 7 ist auf der Seite des new-Operators die erneute Nennung des Datentyps nicht mehr notwendig. Aufgrund der graphische Darstellung von <> spricht auch (fälschlich) vom „Diamond-Operator“ obwohl es sich hier um keinen Operator sondern um eine verkürzte Schreibweise handelt!
Der „Diamond-Operator“ darf überall dort verwendet werden, wo der Compiler eindeutig den Typ ermitteln kann. Die Schreibweise mit der expliziten Angabe des Typs (new Wrapper<Integer>(2)) ist weiter möglich und gültig.
1.3.1.2. Generisches Interface
Wie auch eine Klasse kann auch ein Interface generisch definiert werden.
Beispiel:
Code:
public interface MyQueue<Item> {
public void append(Item i);
public Item remove();
public int length();
}
Eine Implementierung könnte folgendermaßen aussehen:
Code:
public class MyLinkedQueue<Item> implements MyQueue<Item> {
private class Cell {
private Item value;
private Cell next;
public Cell(Item i) {
value = i;
}
}
private Cell front, rear;
@Override
public MyQueue<Item> append(Item i) {
Cell c = new Cell(i);
…
return this;
}
@Override
public Item remove() {
Item ret=null;
if (front!=null){
…
}
return ret;
}
@Override
public int length() {
…
return ret;
}
public static void main(String[] a){
MyLinkedQueue<Integer> q= new MyLinkedQueue<Integer>();
System.out.println("Size: "+q.length());
q.append(4).append(5).append(7);
System.out.println("Size: "+q.length());
int i=1;
while(q.length()>0)
System.out.println("Cell: "+(i++)+ ":"+q.remove());
}
}
1.3.1.3. Objekterzeugung
Die Anzahl der aktuellen Parameter muss gleich der Anzahl der formalen Parameter sein.
Code:
MyLinkedQueue<Integer> q= new MyLinkedQueue<Integer>();
<Integer> ersetzt.
Probleme und mögliche Fehler bei der Erstellung:
Code:
public class Fehler<T> {
T t=new T(); // unexpected type: forbidden
T[] t2=new T[2]; // generic array creation: forbidden
List<String>[] list=new List<String>[3]; // generic array creation: forbidden
}
Code:
public class Fehler {
public static void main(String[] args) {
Number[] a = new Integer[1];
a[0] = new Double( 3.14 ); // <-- no compiler error, but runtime exception!
ArrayList<Number> a = new ArrayList<Integer>(); // <-- compiler error
}
}
Fehler in statischen Teilen:
Code:
public class MyList<T> {
private T head;
private List<T> tail;
public static void main(String[] args) {
List<T> list = new List<T>();
}
}
Daher darf in statischen Teilen einer generischen Klasse Nicht auf die formalen Typparameter der Klasse Bezug genommen werden!
Wichtig:
Generics sind per "Type Erasure" implementiert: Die Typen werden zur Kompilierzeit überprüft und anschließend wird die Typinformation entfernt.
Achtung:
Es gibt generische Klassen, aber keine „generischen Objekte“! Jedes Objekt hat den bei seiner Erzeugung festgelegten Typ.
1.3.1.4. Generische Methoden
Neben Interfaces und Klassen können auch Methoden generisch sein. Sehr praktisch sind solche Methoden in Utility-Klassen.
Beispiel:
Code:
public final static <T> T random( T m, T n ) {
return Math.random() > 0.5 ? m : n;
}
1.3.1.5. Beschränkte Generizität
Falls die Typparameter nicht eingeschränkt werden, ist jede Klasse (bzw. Interface) als aktueller Parameter möglich.
Beispiel:
Code:
public final static <T extends Comparable<T>> T max( T a, T b) {
return (a.compareTo(b)>0)?a:b;
}
Schranken und Wildcards:
<?> beliebiger Typ (vermeidet Warnung: „unbounded wildcard“)
<? super X> Superklasse von X: „lower bounded wildcard“ ist erlaubt
<? extends X> Von X (Klasse oder Interface) abgeleiteter Typ („upper
bounded wildcard“)
<? extends X & Y & Z> Subtyp von mehreren Typen (außer dem ersten Typ
nur Interfaces erlaubt) „multiple bounds“
Unterschied zwischen <? extends X> und <T extends X> ?
Wenn der Typ <T> weiter zum Beispiel innerhalb der Klasse oder Methode benötigt wird, muss die zweite Form gewählt werden. Ansonsten sollte die erste Variante bevorzugt werden.
================================================== ============
[1] [duden.de] polymorph: in verschiedenerlei Gestalt, Form vorhanden, vorkommend; vielgestaltig, verschiedengestaltig
[2] Grundbegriffe objektorientierter Programmierung: Datenkapselung, Vererbung und Polymorphie.
================================================== ============
Ich hoffe ich konnte euch Polymorphie erklären können und viel Spaß beim lernen!






