Java Generics meistern: Sortierung vereinfachen & Typsicherheit erhöhen
Motivation für Generics
Es wäre schön, wenn wir eine einzige Sortiermethode schreiben könnten, die die Elemente in einem Integer-Array, einem String-Array oder einem Array jedes Typs sortieren könnte, der eine Reihenfolge unterstützt.
Java Generic-Methoden und generische Klassen ermöglichen es Programmierern, mit einer einzelnen Methodendeklaration einen Satz verwandter Methoden bzw. mit einer einzelnen Klassendeklaration einen Satz verwandter Typen anzugeben.
Generics bieten auch Compile-Time Type Safety (Kompilierzeit-Typsicherheit), die es Programmierern ermöglicht, ungültige Typen zur Kompilierzeit zu erkennen.
Mit dem Java-Generics-Konzept könnten wir eine generische Methode zum Sortieren eines Objekt-Arrays schreiben und diese generische Methode dann mit Integer-Arrays, Double-Arrays, String-Arrays usw. aufrufen, um die Array-Elemente zu sortieren.
Generische Methoden (Generic Methods)
Sie können eine einzelne generische Methodendeklaration schreiben, die mit Argumenten verschiedener Typen aufgerufen werden kann. Basierend auf den Typen der an die generische Methode übergebenen Argumente behandelt der Compiler jeden Methodenaufruf angemessen. Folgende Regeln gelten für die Definition generischer Methoden:
- Alle generischen Methodendeklarationen haben einen Typparameter-Abschnitt, der von spitzen Klammern (
<und>) begrenzt wird und dem Rückgabetyp der Methode vorausgeht (<E>im nächsten Beispiel). - Jeder Typparameter-Abschnitt enthält einen oder mehrere Typparameter, die durch Kommas getrennt sind. Ein Typparameter, auch als Typvariable bekannt, ist ein Bezeichner, der einen generischen Typnamen spezifiziert.
- Die Typparameter können verwendet werden, um den Rückgabetyp zu deklarieren und als Platzhalter für die Typen der an die generische Methode übergebenen Argumente zu fungieren, die als tatsächliche Typargumenten bekannt sind.
- Der Körper einer generischen Methode wird wie der jeder anderen Methode deklariert. Beachten Sie, dass Typparameter nur Referenztypen darstellen können, keine primitiven Typen (wie
int,doubleundchar).
Beispiel: Array verschiedener Typen mit einer generischen Methode ausgeben
public class GenericMethodTest {
// generische Methode printArray
public static <E> void printArray(E[] inputArray) {
// Array-Elemente anzeigen
for (E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String args[]) {
// Arrays von Integer, Double und Character erstellen
Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
Character[] charArray = {'H', 'E', 'L', 'L', 'O'};
System.out.println("Array integerArray contains:");
printArray(intArray); // Integer-Array übergeben
System.out.println("\nArray doubleArray contains:");
printArray(doubleArray); // Double-Array übergeben
System.out.println("\nArray characterArray contains:");
printArray(charArray); // Character-Array übergeben
}
}
Ausgabe:
Array integerArray contains: 1 2 3 4 5
Array doubleArray contains: 1.1 2.2 3.3 4.4
Array characterArray contains: H E L L O
Begrenzte Typparameter (Bounded Type Parameters)
Es kann Zeiten geben, in denen Sie die Arten von Typen einschränken möchten, die an einen Typparameter übergeben werden dürfen. Zum Beispiel könnte eine Methode, die mit Zahlen arbeitet, nur Instanzen von Number oder dessen Unterklassen akzeptieren wollen. Dafür sind begrenzte Typparameter gedacht.
Um einen begrenzten Typparameter zu deklarieren, listen Sie den Namen des Typparameters auf, gefolgt vom extends-Schlüsselwort, gefolgt von seiner oberen Schranke.
Beispiel: Größtes von drei Comparable-Objekten zurückgeben
Das folgende Beispiel veranschaulicht, wie extends in einem allgemeinen Sinne verwendet wird, um entweder "extends" (wie bei Klassen) oder "implements" (wie bei Interfaces) zu bedeuten. Dieses Beispiel ist eine generische Methode, die das größte von drei Comparable-Objekten zurückgibt:
public class MaximumTest {
// Bestimmt das größte von drei Comparable-Objekten
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // Annahme: x ist initial das größte
if (y.compareTo(max) > 0) {
max = y; // y ist bisher das größte
}
if (z.compareTo(max) > 0) {
max = z; // z ist jetzt das größte
}
return max; // gibt das größte Objekt zurück
}
public static void main(String args[]) {
System.out.printf("Max of %d, %d and %d is %d\n\n", 3, 4, 5, maximum(3, 4, 5));
System.out.printf("Max of %.1f, %.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
System.out.printf("Max of %s, %s and %s is %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
}
}
Ausgabe:
Max of 3, 4 and 5 is 5
Max of 6.6, 8.8 and 7.7 is 8.8
Max of pear, apple and orange is pear
Generische Klassen (Generic Classes)
Eine generische Klassendeklaration sieht aus wie eine nicht-generische Klassendeklaration, außer dass der Klassenname von einem Typparameter-Abschnitt gefolgt wird.
Wie bei generischen Methoden kann der Typparameter-Abschnitt einer generischen Klasse einen oder mehrere Typparameter haben, die durch Kommas getrennt sind. Diese Klassen werden als parameterisierte Klassen oder parameterisierte Typen bezeichnet, da sie einen oder mehrere Parameter akzeptieren.
Beispiel: Generische Klasse definieren
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value: %d\n\n", integerBox.get());
System.out.printf("String Value: %s\n", stringBox.get());
}
}
Ausgabe:
Integer Value: 10
String Value: Hello World
Java
- Java Primitive Datentypen: Vollständiger Leitfaden mit Beispielen
- Java Operatoren meistern: Typen, Syntax und praktische Beispiele
- Java Kommentare: Typen, Verwendung und Best Practices
- Java Enhanced For-Schleife meistern: Syntax, Beispiele und Best Practices
- Java Strings meistern: Erstellung, Methoden und Best Practices
- Java-Schnittstellen meistern: Konzepte, Implementierung und Best Practices
- Java Try-With-Resources meistern: Automatisches Ressourcenmanagement erklärt
- Java Annotations erklärt: Typen, Platzierung und praktische Beispiele
- Java Assertions: Praktischer Leitfaden zur Fehlererkennung und Code-Reliabilität
- Java Type Casting meistern: Von Widening zu Narrowing und darüber hinaus