Object und Wrapper | Java | Java Collection Framework |
Manchmal möchte man Klassen schaffen, die mit unterschiedlichen Typen umgehen können. Dazu gehören beispielsweise Container wie die Java-Collection. Betrachten wir eine sehr einfache Klasse Paar. Sie soll ein ganzzahliges Variablenpaar aufnehmen.
class Paar { int i1, i2; public Paar(int i1, int i2) { this.i1 = i1; this.i2 = i2; } }Sollten wir ein weiteres Paar mit Strings benötigen, müssten wir eine neue Klasse schaffen. Dummerweise dürften wir den Klassennamen nicht wiederverwenden, auch wenn er eigentlich auch für Stringt sehr passend ist.
class StringPaar { String i1, i2; public StringPaar(String i1, String i2) { this.i1 = i1; this.i2 = i2; } }Aber da wir wissen, dass die Klasse Object die Basisklasse aller Klasse ist, können wir die Elemente als Object definieren. Und schon können wir alle beliebigen Typen paarweise ablegen.
class Paar { Object i1, i2; public Paar(Object i1, Object i2) { this.i1 = i1; this.i2 = i2; } }Im Testlauf funktioniert nun beides, Zahlen und auch Strings:
public class PaarTest { public static void main(String[] args) { Paar paar = new Paar(1,3); Paar str = new Paar("Laurel", "Hardy"); } }Leider funktioniert nun aber auch dies:
Paar nichtgut = new Paar("Pferd", 3.14159);
Typsicherheit durch Typvariablen
Java ermöglicht es, die Klasse Paar so zu definieren, dass beim Anlegen eines Objekts festgelegt wird, mit welche Klassentypen die Klasse Paar akzeptiert. Im Gegensatz zur Verwendung von Object entsteht dadurch eine Typsicherheit. Dazu verwendet man Generics mit Typvariablen.Syntaktisch wird dem Klassenname in spitzen Klammern (Kleiner- und Größerzeichen) eine Variable mit einer Typvariablen gekennzeichnet. Innerhalb der Klasse wird dieser Name anstelle des Typen verwendet. Man verwendet dazu einen einzelnen Großbuchstaben, meist T. Das ist aber lediglich Konvention.
Wir können also die Klasse Paar übernehmen und statt Object ein T verwenden und dieses am Klassennamen mit spitzen Klammern einfügen.
class Paar<T> { T i1, i2; public Paar(T i1, T i2) { this.i1 = i1; this.i2 = i2; } }Tatsächlich funktionieren alle Aufrufe in PaarTest nach wie vor. Um eine Typsicherheit zu erlangen, muss der Typ bei der Instanziierung angegeben werden.
public class PaarTest { public static void main(String[] args) { Paar<Integer> paar = new Paar<Integer>(1,3); Paar<String> str = new Paar<>("haa", "huh"); Paar<String> nichtgut = new Paar("Pferd", 3.14159); } }
- Sie sehen in der ersten Zeile, dass Sie die Wrapper-Klasse Integer statt int verwenden müssen. Als Typvariablen sind die primitiven Typen nicht erlaubt.
- In der zweiten Zeile wird String verwendet. Sie sehen, dass die spitzen Klammern beim new leer sind. Das ist zulässig. Der Compiler ergänzt selbstständig das fehlende Wort String.
- In der dritten Zeile wurde keine spitze Klammer hinter dem new angegeben. Dann wird nach wie vor Object verwendet und die Typsicherheit ist nicht gewährleistet. Sobald Sie aber versuchen, die spitzen Klammern zu setzen, wird der Compiler sofort melden, dass die Paramter des Konstruktors unvereinbar mit dem Typ sind.
Typvorgaben durch Interfaces
Die Möglichkeiten einer generischen Klasse sind oft beschränkt, da nur Methoden erlaubt sind, die allen Klassen gemein sind. Beispielsweise gibt es bei der folgenden Klasse, die eine Minimumfunktion zur Verfügung stellen will, Schwierigkeiten, da Klassen das Kleinerzeichen nicht kennen.public class MinimumStattdessen kann eine Methode less verwendet werden, die allerdings den Nachteil hat, dass auch sie den meisten Klassen nicht bekannt ist. Allerdings kann man mit einem Interface erzwingen, dass alle Klassen, die Minimum verwenden, eine Methode less implementieren. Im ersten Schritt wird die Abhängigkeit im Klassenkopf deklariert.{ boolean min(T a, T b) { boolean kleiner = false; if (a < b) { // Compilerfehler, da Klassen Kleiner nicht kennen kleiner = true; } return kleiner; } }
public class Minimum<T extends Comparable<T>> { boolean min(T a, T b) { boolean kleiner = true; if (a.compareTo(b)>0) { kleiner = false; } return kleiner; } }Das Interface Comparable wird sowohl von String als auch von Integer implementiert. Die Aufrufe würden folgendermaßen aussehen:
Minimum<String> m = new Minimum<>(); System.out.println(m.min("C", "B")); Minimum<Integer> n = new Minimum<>(); System.out.println(n.min(1, 3));Eigene Klassen müssten Comparable implementieren und dazu die Methode compareTo definieren, um die Klasse Minimum nutzen zu können.
Video
Object und Wrapper | Java | Java Collection Framework |