Beispiel: Von einer nicht generischen Klasse zu einer generischen Klasse

 Die Klassen MerkerX implementieren eine Warteschlange der Länge 2. Sie ist in der Lage sich den letzten und den vorletzten Wert zu merken.

Im Folgenden erhalten die Klassen neue Namen (Merker2, Merker3 etc.). Dies erlaubt die Klassen gleichzeitig im Paket zu verwalten

Von Basistypen zu Objekten

Die Klasse Merker1 ist in der Lage Basistypen vom Typ int zu verwalten. Die Werte werden als Teil der Instanzen  der Klasse Merker1 verwaltet.

Die Klasse Merker2 verwaltet Objekte vom Typ Integer. Verschiedene Instanzen von Merker2 könne also auf die gleichen Instanzen der Klasse Integer zeigen.

Klasse Merker1 Klasse Merker2
package Kurs2.Generics;

public class Merker1 {
private int letzter;
private int vorletzter;

public Merker1() {
letzter = 0;
vorletzter = 0;
}

public int getLetzter() {
return letzter;
}

public int getVorLetzter() {
return vorletzter;
}

public void merke(int wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "[" + letzter + ";" + vorletzter + "]";
}

public static void main(String[] args) {
// Teil 1: int Verwalten
Merker1 f1 = new Merker1();
int i10 = 10;
f1.merke(i10);
int i11 = 11; // Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);//Erz. mit Autoboxing
f1.merke(i12);
System.out.println(f1);

// Teil 2: int verwalten
Merker1 f2 = new Merker1();
int i20 = 100;
f2.merke(i20);
int i21 = 101;
f2.merke(i21);
int i22 = 102;
f2.merke(i22);
System.out.println(f2);

// Teil 3: int verwalten
Merker1 f3 = new Merker1();
int i30 = 200;
f3.merke(i30);
int i31 = 201;
f3.merke(i31);
int i32 = 202;
f3.merke(i32);
System.out.println(f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

package Kurs2.Generics;

public class Merker2 {
private Integer letzter;
private Integer vorletzter;

public Merker2() {
letzter = null;
vorletzter = null;
}

public Integer getLetzter() {
return letzter;
}

public Integer getVorLetzter() {
return vorletzter;
}

public void merke(Integer wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "[" + letzter + ";" + vorletzter + "]";
}

public static void main(String[] args) {
// Teil 1: Integer Verwalten
Merker2 f1 = new Merker2();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println(f1);

// Teil 2: Integer verwalten
Merker2 f2 = new Merker2();
int i20 = 100;
f2.merke(i20);
int i21 = 101;
f2.merke(i21);
int i22 = 102;
f2.merke(i22);
System.out.println(f2);

// Teil 3: Integer verwalten
Merker2 f3 = new Merker2();
int i30 = 200;
f3.merke(i30);
int i31 = 201;
f3.merke(i31);
int i32 = 202;
f3.merke(i32);
System.out.println(f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

Von Integer-Objekten zur Verwaltung beliebiger Objekte

Die Klasse Merker3 kann nun nicht nur Instanzen der Klasse Integer verwalten. Sie kann beliebige Objekte verwalten.

Klasse Merker2 Klasse Merker3
package Kurs2.Generics;

public class Merker2 {
private Integer letzter;
private Integer vorletzter;

public Merker2() {
letzter = null;
vorletzter = null;
}

public Integer getLetzter() {
return letzter;
}

public Integer getVorLetzter() {
return vorletzter;
}

public void merke(Integer wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "[" + letzter + ";" + vorletzter + "]";
}

public static void main(String[] args) {
// Teil 1: Integer Verwalten
Merker2 f1 = new Merker2();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println(f1);

// Teil 2: Integer verwalten
Merker2 f2 = new Merker2();
int i20 = 100;
f2.merke(i20);
int i21 = 101;
f2.merke(i21);
int i22 = 102;
f2.merke(i22);
System.out.println(f2);

// Teil 3: Integer verwalten
Merker2 f3 = new Merker2();
int i30 = 200;
f3.merke(i30);
int i31 = 201;
f3.merke(i31);
int i32 = 202;
f3.merke(i32);
System.out.println(f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

package Kurs2.Generics;

public class Merker3 {
private Object letzter;
private Object vorletzter;

public Merker3 () {
letzter = null;
vorletzter = null;
}

public Object getLetzter() {
return letzter;
}

public Object getVorLetzter() {
return vorletzter;
}

public void merke(Object wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker3 f1 = new Merker3();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker3 f2 = new Merker3();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker3 f3 = new Merker3();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
f3.merke(i32);
System.out.println (f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

Von der unsicheren Verwaltung beliebiger Objekte zu einer generischen Klasse

Die Klasse Merker4 ist jetzt generisch. Sie kann typsicher Objekte verwalten. In der main() Methode der Klassse Merker4 wird jedoch diese neue gewonnen Fähigkeit ignoriert. Die Klasse Merker4 wird benutzt wie eine Klasse die beliebige Typen verwalten kann.

Klasse Merker3 Klasse Merker4
package Kurs2.Generics;
public class Merker3 {
private Object letzter;
private Object vorletzter;

public Merker3 () {
letzter = null;
vorletzter = null;
}

public Object getLetzter() {
return letzter;
}

public Object getVorLetzter() {
return vorletzter;
}

public void merke(Object wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker3 f1 = new Merker3();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker3 f2 = new Merker3();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker3 f3 = new Merker3();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
f3.merke(i32);
System.out.println (f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

package Kurs2.Generics;
public class Merker4<T> {
private T letzter;
private T vorletzter;

public Merker4 () {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker4 f1 = new Merker4();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11); //Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12); //Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker4 f2 = new Merker4();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker4 f3 = new Merker4();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
f3.merke(i32);
System.out.println (f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;

}
}

Typsichere Nutzung der generischen Klasse

Die Klasse Merker5 ist mit Ausnahme der main() Methode identisch zur Klasse Merker4. In der main() Methode der Klassse Merker4 werden jetzt Objekte f1, f2, f3 angelegt die nur die Verwaltung von bestimmten Typen in der Klasse Merker5 erlauben. Da die aktuellen Typen von f1, f2, f3 unterschiedlich sind kann man Sie nicht mehr beliebig aufeinander zuweisen.

Klasse Merker4 Klasse Merker5
package Kurs2.Generics;
public class Merker4<T> {
private T letzter;
private T vorletzter;

public Merker4 () {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker4 f1 = new Merker4();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11); //Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12); //Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker4 f2 = new Merker4();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker4 f3 = new Merker4();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
f3.merke(i32);
System.out.println (f3);

// Warum sind diese Zuweisungen erlaubt ?
f1 = f2;
f2 = f3;
f1 = f3;
}
}

package Kurs2.Generics;
public class Merker5 <T> {
private T letzter;
private T vorletzter;

public Merker5 () {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker5<Integer> f1 = new Merker5<Integer>();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker5<Float> f2 = new Merker5<Float>();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker5<Integer> f3 = new Merker5<Integer>();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
//f3.merke(i31);
String i32 = "Zeichenkette";
//f3.merke(i32);
System.out.println (f3);

// Welche Zuweisungen sind erlaubt ?
//f1 = f2;
//f2 = f3;
//f1 = f3;

}
}

Die Klassenhierarchie der verschiedenen Varianten der generischen Klasse Merker5:

 

Einschränkung der generischen Klasse Merker6 auf bestimmte Typen

Die Klasse Merker6 verwendet das Schlüsselwort extends um die Verwendung auf zahlenartige Typen (Number) einzuschränken. Die Einschränkungen bei den Zuweisungen zwischen den Variablen f1, f2, und f3 sind zu beachten.

Anbei die Klassen Hierarchie des Java API zur Klasse Number und ihrer Unterklassen:

Klasse Merker4 Klasse Merker5
package Kurs2.Generics;
public class Merker5 <T> {
private T letzter;
private T vorletzter;

public Merker5 () {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "["+ letzter +";" + vorletzter + "]";
}

public static void main (String[] args) {
// Teil 1: Integer Verwalten
Merker5<Integer> f1 = new Merker5<Integer>();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11);// Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12);// Reguläre Erzeugung
f1.merke(i12);
System.out.println (f1);

// Teil 2: Float verwalten
Merker5<Float> f2 = new Merker5<Float>();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println (f2);

// Teil 3: Alles Verwalten
Merker5<Integer> f3 = new Merker5<Integer>();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
//f3.merke(i31);
String i32 = "Zeichenkette";
//f3.merke(i32); // Warum ist dies nicht erlaubt?
System.out.println (f3);

// Welche Zuweisungen sind erlaubt ?
//f1 = f2;
//f2 = f3;
//f1 = f3;

}
}

package Kurs2.Generics;
public class Merker6<T extends Number> {
    private T letzter;
    private T vorletzter;

public Merker6() {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "[" + letzter + ";" + vorletzter + "]";
}

public static void main(String[] args) {
// Teil 1: Integer Verwalten
Merker6<Integer> f1 = new Merker6<Integer>();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11); // Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12); // Reguläre Erzeugung
f1.merke(i12);
System.out.println(f1);

// Teil 2: Float verwalten
Merker6<Float> f2 = new Merker6<Float>();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println(f2);

// Teil 3: Alles Verwalte
Merker6<Number> f3 = new Merker6<Number>();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
//f3.merke(i32); // Warum ist dies nicht erlaubt?
System.out.println(f3);

// f1 = f2; // Warum ist dies nicht erlaubt?
// f2 = f3; // Warum ist dies nicht erlaubt?
//
}
}

Wild Cards

Die Klasse Merker7 benutzt in der main() Methode eine Variable merkeAlles die mit einer Wildcard versehen ist.

Die Variable kann auf alle Varianten der Klasse Merker7 zeigen, sie kann aber nicht alle Methoden nutzen da die Methoden zur Übersetzungszeit nicht bekannt sein müssen.

package Kurs2.Generics;
public class Merker7<T extends Number> {

private T letzter;
private T vorletzter;

public Merker7() {
letzter = null;
vorletzter = null;
}

public T getLetzter() {
return letzter;
}

public T getVorLetzter() {
return vorletzter;
}

public void merke(T wert) {
vorletzter = letzter;
letzter = wert;
}

public String toString() {
return "[" + letzter + ";" + vorletzter + "]";
}

public static void main(String[] args) {
// Teil 1: Integer Verwalten
Merker7<Integer> f1 = new Merker7<Integer>();
Integer i10 = 10; // Erzeugung mit Autoboxing
f1.merke(i10);
Integer i11 = new Integer(11); // Reguläre Erzeugung
f1.merke(i11);
Integer i12 = new Integer(12); // Reguläre Erzeugung
f1.merke(i12);
System.out.println(f1);

// Teil 2: Float verwalten
Merker7<Float> f2 = new Merker7<Float>();
Float i20 = 100.1f;
f2.merke(i20);
Float i21 = 101.2f;
f2.merke(i21);
Float i22 = 102.3f;
f2.merke(i22);
System.out.println(f2);

// Teil 3: Alles Verwalten
Merker7<Number> f3 = new Merker7<Number>();
Integer i30 = 200;
f3.merke(i30);
Float i31 = 201.f;
f3.merke(i31);
String i32 = "Zeichenkette";
//f3.merke(i32); // Warum ist dies nicht erlaubt?
System.out.println(f3);

// Teil 4: Alles Verwalten
Merker7 f4 = new Merker7();
Integer i40 = 400;
f4.merke(i40);
Float i41 = 401.f;
f4.merke(i41);
String i42 = "Zeichenkette";
//f4.merke(i42); // Warum ist dies nicht erlaubt?
System.out.println(f4);
f2 = f4;

Merker7<?> merkeAlles;

merkeAlles = f1;

// Hier wird implizit die Methode toString() aufgerufen
System.out.println(merkeAlles);
int m;
// Warum ist diese Zuweisung nicht erlaubt?
//m = merkeAlles.letzter;
// Warum ist diese Zuweisung erlaubt?
m = 3 * f1.letzter;

System.out.println(merkeAlles.letzter);
merkeAlles = f2;
System.out.println(merkeAlles);
merkeAlles = f3;
System.out.println(merkeAlles);
}
}