9 Generics – Generische Datentypen
9.1 Problembeschreibung
In der Programmierung dienen Variablen als Platzhalter für Daten. Die Idee, die hinter den Generics steckt, geht noch einen konsequenten Schritt weiter. Generics sind ebenfalls Platzhalter, allerdings für Datentypen. Mit Generics lassen sich Klassen und Methoden definieren, für die bestimmte Datentypen geprägt sind. Lassen Sie mich diese Aussage sofort an dem konkreten Beispiel zeigen. Dazu dient die benutzerdefinierte Klasse Stack mit den beiden Methoden Push und Pop als Ausgangspunkt der Überlegungen. Die Klasse sei zunächst noch »klassisch« definiert.
class Stack {
private readonly int size;
private Object[] elements;
private int pointer = 0;
public Stack(int size) {
this.size = size;
elements = new Object[size];
}
public void Push(Object element) {
if (pointer >= this.size)
throw new StackOverflowException();
elements[pointer] = element;
pointer++;
}
public Object Pop() {
pointer--;
if (pointer >= 0)
return elements[pointer];
else {
pointer = 0;
throw new InvalidOperationException("Der Stack ist leer");
}
}
public int Length {
get { return this.pointer; }
}
}
Listing 9.1 Die Definition einer Klasse, die einen allgemeinen Stack beschreibt
Die internen Daten werden in einem Object-Array gespeichert. Instanziiert werden kann diese Klasse über den Aufruf des einfach parametrisierten Konstruktors, dem ein int übergeben wird, mit dem die Größe des internen Arrays initialisiert wird.
Mit Push wird der Stack-Instanz ein Objekt übergeben. Sollte zu diesem Zeitpunkt die Kapazität des Arrays bereits ausgeschöpft sein, ist eine Ausnahme die Folge. Mit einer Ausnahme reagiert auch die Methode Pop, falls ein weiteres Element abgerufen wird, das Array aber bereits geleert ist und kein weiteres Element mehr enthält.
Jedes vom Stack-Objekt im Array verwaltete Element ist vom Typ Object. Das ist für eine Auswertung jedoch nicht präzise genug, was dazu führt, dass das Element in den richtigen Typ konvertiert werden muss. Das folgende Codefragment soll das verdeutlichen:
Stack stack = new Stack(10);
stack.Push(2);
int str = (int)stack.Pop();
Würden wir versuchen, das vom Stack geholte Element beispielsweise in eine Zeichenfolge zu konvertieren, also
string str = (string)stack.Pop();
wäre das Auslösen einer Ausnahme vom Typ InvalidCastException die unweigerliche Folge. Wie Sie sehen können, birgt die Flexibilität des Typs Object gravierende Nachteile in sich. Sie können natürlich versuchen, mehrere Klassen zu entwickeln, die auf einen bestimmten Datentyp spezialisiert sind, z. B.:
public class StackInt {
private int[] elemente
public void Push(int number) {...}
[...]
}
Damit wäre gewährleistet, dass ein Objekt dieser Klasse nur Integer verwalten kann. Die Auswertung der einzelnen Objekte macht somit kein Problem. Andererseits führt dieser Ansatz im Extremfall zu einer großen Anzahl ähnlicher Klassen, die jeweils nur für einen spezifischen Typ geeignet sind. Solche Lösungen sind zwar umsetzbar, allerdings schlecht zu warten. Zudem könnte sich in naher oder ferner Zukunft der Bedarf nach einem weiteren Stack mit einem noch nicht berücksichtigten Typ ergeben, so dass man rückblickend mit dem Ergebnis nicht zufrieden wäre. Genau an diesem Punkt spielen Generics ihre ganze Stärke aus.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.