Die sogenannten Generics: Sie ermöglichen die Angabe von Typparametern beim Definieren von Klassen und Schnittstellen. Dieser Typparameter wird beim Deklarieren von Variablen und beim Erstellen von Objekten ermittelt (d. h. bei der Übergabe tatsächlicher Typparameter, die auch als Typargumente bezeichnet werden können).
Generische Klasse oder Schnittstelle
Der Kopiercode der „Diamant“-Syntax lautet wie folgt:
//Definition
öffentliche Schnittstelle List<E> erweitert Collection<E>
Die öffentliche Klasse HashMap<K,V> erweitert AbstractMap<K,V> und implementiert Map<K,V>, Cloneable, Serializable
//verwenden
List<String> list = new ArrayList();
//Nach Java 7 können Sie den Typparameter der spitzen Klammern dahinter weglassen.
List<String> list = new ArrayList<>();
Leiten Sie eine Unterklasse von einer generischen Klasse ab
Kopieren Sie den Codecode wie folgt:
//Methode 1
Die öffentliche Klasse App erweitert GenericType<String>
//Methode 2
Die öffentliche Klasse App<T> erweitert GenericType<T>
//Methode 3
Die öffentliche Klassen-App erweitert GenericType
Pseudo-Generika
Es gibt keine echte generische Klasse. Die JVM kennt die Existenz generischer Klassen nicht anders als gewöhnliche Klassen in statischen Methoden, statischen Initialisierungsblöcken und statischen Variablen.
- Die folgenden Methoden sind alle falsch. Der Code zum Kopieren lautet wie folgt:
private statische T-Daten;
statisch{
Tf;
}
öffentliche statische Leere func(){
T-Name = 1;
}
Das folgende Beispiel kann von der Seite aus überprüfen, ob es keine generische Klasse gibt. Der Code lautet wie folgt:
public static void main(String[] args){
List<String> a1 = new ArrayList<>();
List<Integer> a2 = new ArrayList<>();
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass());
System.out.println(a2.getClass());
}
Der ausgegebene Kopiercode lautet wie folgt:
WAHR
Klasse java.util.ArrayList
Klasse java.util.ArrayList
Geben Sie Platzhalter ein
Zunächst muss klar sein, dass, wenn Foo die übergeordnete Klasse von Bar ist, List<Foo> jedoch nicht die übergeordnete Klasse von List<Bar> ist, um verschiedene generische übergeordnete Klassen darzustellen generische Platzhalter. Das heißt, List<?> stellt die übergeordnete Klasse verschiedener generischer Listen dar. Mit dieser Art von Platzhaltern können List-Generika keine Elemente festlegen (setzen), sondern nur Elemente abrufen (abrufen). Da das Programm die Typen in der Liste nicht ermitteln kann, kann es keine Objekte hinzufügen. Das erhaltene Objekt muss jedoch vom Typ Objekt sein.
Die folgenden Methoden werden mit Fehlern kompiliert:
Kopieren Sie den Codecode wie folgt:
List<?> list = new ArrayList<>();
list.add(new Object());
Einige Ideen:
1. List<String>-Objekte können nicht als List<Object>-Objekte verwendet werden, das heißt: Die List<String>-Klasse ist keine Unterklasse der List<Object>-Klasse.
2. Arrays unterscheiden sich von Generika: Angenommen, Foo ist ein Untertyp (Unterklasse oder Unterschnittstelle) von Bar, dann ist Foo[] immer noch ein Untertyp von Bar[], aber G<Foo> ist kein G<Bar>-Untertyp.
3. Um die übergeordnete Klasse verschiedener generischer Listen darzustellen, müssen wir Typ-Wildcards verwenden. Der Typ-Wildcard ist ein Fragezeichen (?). Übergeben Sie ein Fragezeichen als Typargument an die List-Auflistung. > (bedeutet unbekannte Liste von Typelementen). Dieses Fragezeichen (?) wird als Platzhalterzeichen bezeichnet und sein Elementtyp kann mit jedem beliebigen Typ übereinstimmen.
Obergrenze für Platzhalter
List<? erweitert SuperType> repräsentiert die übergeordnete Klasse oder sich selbst aller generischen SuperType-Listen. Generika mit Platzhalter-Obergrenzen können keine Set-Methoden haben, sondern nur Get-Methoden.
Durch Festlegen der Obergrenze für Platzhalter können die folgenden Probleme gelöst werden: Dog ist eine Unterklasse von Animal und es gibt eine getSize-Methode, um die Anzahl der eingehenden Listen abzurufen. Der Code lautet wie folgt:
abstrakte Klasse Tier {
öffentliches abstraktes void run();
}
Klasse Hund erweitert Tier {
public void run() {
System.out.println("Hundelauf");
}
}
öffentliche Klassen-App {
public static void getSize(List<Animal> list) {
System.out.println(list.size());
}
public static void main(String[] args) {
List<Dog> list = new ArrayList<>();
getSize(list); // Kompilierungsfehler hier
}
}
Der Grund für den Programmierfehler liegt hier darin, dass List<Animal> nicht die übergeordnete Klasse von List<Dog> ist. Die erste Lösung besteht darin, den formalen Parameter List<Animal> in der getSize-Methode in List<?> zu ändern. In diesem Fall ist jedoch bei jedem Abrufen des Objekts eine erzwungene Typkonvertierung erforderlich, was problematischer ist. Die Verwendung der Platzhalterobergrenze löst dieses Problem sehr gut. Sie können List<Animal> in List<? erweitert Animal> ändern, und es treten keine Fehler bei der Kompilierung auf und es ist keine Typkonvertierung erforderlich.
Untergrenze für Platzhalter
List<? super SubType> stellt die untere Grenze der generischen SubType-Liste dar. Generika mit Platzhalter-Obergrenzen können keine Get-Methoden haben, sondern nur Set-Methoden.
Generische Methoden
Wenn Sie Klassen und Schnittstellen definieren, ohne Typparameter zu verwenden, aber beim Definieren von Methoden selbst Typparameter definieren möchten, bietet JDK1.5 auch Unterstützung für generische Methoden. Die Methodensignatur einer generischen Methode enthält mehr Typparameterdeklarationen als die Methodensignatur einer gewöhnlichen Methode. Die Typparameterdeklarationen werden durch Kommas (,) getrennt Modifikatoren und Methodenrückgabewerttypen Das Syntaxformat ist wie folgt:
Kopieren Sie den Codecode wie folgt:
Methodenname des Modifikator-Rückgabewerttyps (Typliste) {
//Methodenkörper
}
Mit generischen Methoden können Typparameter verwendet werden, um Typabhängigkeiten zwischen einem oder mehreren Parametern einer Methode oder zwischen Methodenrückgabewerten und Parametern auszudrücken. Wenn keine solche Typabhängigkeit besteht, sollten generische Methoden nicht verwendet werden. Die Kopiermethode von Collections verwendet generische Methoden:
Kopieren Sie den Codecode wie folgt:
public static <T> void copy(List<? super T> dest, List<? erweitert T> src){ ...}
Diese Methode erfordert, dass der src-Typ eine Unterklasse des dest-Typs oder sich selbst sein muss.
Löschen und konvertieren
In streng generischem Code sollten Klassen mit generischen Deklarationen immer Typparameter haben. Um jedoch mit altem Java-Code konsistent zu sein, ist es auch erlaubt, Klassen mit generischen Deklarationen ohne Angabe von Typparametern zu verwenden. Wenn für diese generische Klasse kein Typparameter angegeben ist, wird der Typparameter als Rohtyp bezeichnet und verwendet standardmäßig den ersten Obergrenzentyp, der bei der Deklaration des Parameters angegeben wurde.
Wenn ein Objekt mit generischen Informationen einer anderen Variablen ohne generische Informationen zugewiesen wird, werden alle Typinformationen zwischen den spitzen Klammern verworfen. Wenn beispielsweise ein List<String>-Typ in eine Liste konvertiert wird, wird die Typprüfung der Auflistungselemente der Liste zur Obergrenze der Typvariablen (d. h. Objekt). Diese Situation wird als Löschen bezeichnet.
Der Beispielkopiercode lautet wie folgt:
Klasse Apple<T erweitert Number>
{
T-Größe;
publicApple()
{
}
öffentlicher Apfel (T-Größe)
{
this.size = Größe;
}
public void setSize(T-Größe)
{
this.size = Größe;
}
öffentliches T getSize()
{
return this.size;
}
}
öffentliche Klasse ErasureTest
{
public static void main(String[] args)
{
Apple<Integer> a = new Apple<>(6); // ①
// Die getSize-Methode von a gibt ein Integer-Objekt zurück
Ganzzahl as = a.getSize();
// Weisen Sie der Apple-Variablen ein Objekt zu und verlieren Sie dabei die Typinformationen in den spitzen Klammern
Apfel b = a; // ②
// b weiß nur, dass die Art der Größe Zahl ist
Zahlengröße1 = b.getSize();
//Der folgende Code verursacht einen Kompilierungsfehler
Ganzzahlige Größe2 = b.getSize(); // ③
}
}