Typparameter
Seit wir angefangen haben, Listen zu verwenden, haben wir den Datenstrukturen den Typ der Werte zugewiesen, die sie speichern sollen. Beispielsweise wurde eine Liste, die Zeichenketten speichert, als ArrayList<String>
definiert, und eine HashMap, die Schlüssel und Werte als Zeichenketten speichert, wurde als HashMap<String, String>
definiert. Wie in aller Welt können Sie eine Klasse implementieren, die Objekte eines bestimmten Typs enthalten kann?
Generics bezieht sich auf die Art und Weise, wie Klassen, die Objekte speichern, Objekte eines frei wählbaren Typs speichern können. Die Wahl basiert auf dem generischen Typparameter in der Klassendefinition, der es ermöglicht, den Typ zum Zeitpunkt der Objekterstellung zu wählen. Die Verwendung von Generics erfolgt in der folgenden Weise: Nach dem Namen der Klasse folgen ein oder mehrere Typparameter, die jeweils zwischen den Zeichen 'kleiner als' und 'größer als' platziert werden, etwa so: public class Class<TypeParameter1, TypeParameter2, ...>
. Die Typparameter werden normalerweise mit einem einzelnen Zeichen definiert.
Implementieren wir unsere eigene generische Klasse Locker
, die ein Objekt eines beliebigen Typs halten kann.
public class Locker<T> {
private T element;
public void setValue(T element) {
this.element = element;
}
public T getValue() {
return element;
}
}
Die Definition public class Locker<T>
zeigt an, dass der Klasse Locker
ein Typparameter im Konstruktor übergeben werden muss. Nach dem Ausführen des Konstruktoraufrufs werden alle Variablen, die in diesem Objekt gespeichert sind, den Typ haben, der mit dem Konstruktor übergeben wurde. Erstellen wir ein Locker zur Speicherung von Zeichenketten.
Locker<String> string = new Locker<>();
string.setValue(":)");
System.out.println(string.getValue());
:)
In dem obigen Programm sieht die Laufzeit-Implementierung des Locker
-Objekts namens string
wie folgt aus:
public class Locker<String> {
private String element;
public void setValue(String element) {
this.element = element;
}
public String getValue() {
return element;
}
}
Durch Ändern des Typparameters können Sie Locker erstellen, die Objekte anderer Typen speichern. Sie könnten beispielsweise eine ganze Zahl folgendermaßen speichern:
Locker<Integer> integer = new Locker<>();
integer.setValue(5);
System.out.println(integer.getValue());
5
Ähnlich können Sie ein Locker erstellen, um ein Random
-Objekt zu speichern:
Locker<Random> random = new Locker<>();
random.setValue(new Random());
System.out.println(random.getValue().nextDouble());
Es gibt keine maximale Anzahl von Typparametern; es hängt alles von der Implementierung ab. Der oder die Programmierende könnte die folgende Pair
-Klasse implementieren, die zwei Objekte eines festgelegten Typs speichern kann.
public class Pair<T, K> {
private T first;
private K second;
public void setValues(T first, K second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return this.first;
}
public K getSecond() {
return this.second;
}
}
Loading...
Ein wesentlicher Teil der Java-Datenstrukturen verwendet Typparameter, was es ihnen ermöglicht, verschiedene Typen von Variablen zu handhaben. ArrayList erhält beispielsweise einen Typparameter, während HashMap zwei erhält.
List<String> strings = new ArrayList<>();
Map<String, String> keyValuePairs = new HashMap<>();
Von jetzt an, wenn Sie den Typ ArrayList<String>
sehen, wissen Sie, dass seine interne Implementierung einen generischen Typparameter verwendet. Dasselbe Prinzip gilt beispielsweise für das Interface Comparable.
Die Erstellung generischer Schnittstellen ist der Erstellung generischer Klassen sehr ähnlich. Unten können Sie das generische Interface List
studieren (unsere eigene Definition, die nicht so umfangreich ist wie die existierende Java-List).
public interface List<T> {
void add(T value);
T get(int index);
T remove(int index);
}
Es gibt zwei Möglichkeiten für eine Klasse, ein generisches Interface zu implementieren. Eine besteht darin, den Typparameter in der Klassendefinition festzulegen, und die andere besteht darin, die implementierende Klasse ebenfalls mit einem Typparameter zu definieren. Im Folgenden definiert die Klasse MovieList
den Typparameter, wenn sie List implementiert. Die MovieList ist nur für die Verwaltung von Filmen gedacht.
public class MovieList implements List<Movie> {
// Objektvariablen
@Override
public void add(Movie value) {
// Implementierung
}
@Override
public Movie get(int index) {
// Implementierung
}
@Override
public Movie remove(int index) {
// Implementierung
}
}
Die Alternative besteht darin, einen Typparameter in der Klassendefinition zu verwenden, in welchem Fall der Parameter an das Interface weitergegeben wird. Jetzt bleibt diese konkrete Implementierung des Interface generisch.
public class GeneralList<T> implements List<T> {
// Objektvariablen
@Override
public void add(T value) {
// Implementierung
}
@Override
public T get(int index) {
// Implementierung
}
@Override
public T remove(int index) {
// Implementierung
}
}
Wenn Sie möchten, könnten Sie auch die bestehende ArrayList-Klasse verwenden, um die GeneralList zu implementieren. Die Implementierung würde ungefähr so aussehen:
import java.util.ArrayList;
public class GeneralList<T> implements List<T> {
ArrayList<T> values;
public GeneralList() {
this.values = new ArrayList<>();
}
@Override
public void add(T value) {
this.values.add(value);
}
@Override
public T get(int index) {
return this.values.get(index);
}
@Override
public T remove(int index) {
T value = this.values.get(index);
this.values.remove(index);
return value;
}
}