Dieser Artikel bietet Ihnen eine Einführung in das Wissen über Java-Array-Kovarianz und generische Invarianz (mit Code). Ich hoffe, dass er für Sie hilfreich ist. helfen.
Variabilität ist eine große Falle der OOP-Sprachinvarianz, und die Array-Kovarianz von Java ist eine der alten Fallstricke. Da ich kürzlich darauf getreten bin, habe ich mir eine Notiz gemacht. Lassen Sie uns übrigens auch die Degeneration von Paradigmen erwähnen.
Bevor Sie die Array-Kovarianz erklären, klären Sie zunächst drei verwandte Konzepte: Kovarianz, Invarianz und Kontravarianz.
1. Kovarianz, Invarianz, Kontravarianz
Angenommen, ich habe so einen Code für ein Restaurant geschrieben
class Soup<T> {
public void add(T t) {}
}
class Vegetable { }
class Carrot extends Vegetable { }
Nach dem Login kopieren
Es gibt eine generische Klasse Soup, die eine mit Zutat T zubereitete Suppe darstellt, und ihre Methode add(T t) stellt das Hinzufügen zur Suppe Add dar Zutat T. Die Gemüseklasse repräsentiert Gemüse und die Carrot-Klasse repräsentiert Karotten. Natürlich ist Karotte eine Unterklasse von Gemüse.
Dann stellt sich die Frage: Welche Beziehung besteht zwischen Suppe und Suppe?
Die erste Reaktion ist, dass Suppe eine Unterklasse von Suppe sein sollte, da Karottensuppe offensichtlich eine Gemüsesuppe ist. Wenn das der Fall ist, schauen Sie sich den folgenden Code an. Tomate bedeutet Tomaten, eine weitere Unterklasse von Gemüse
Soup<Vegetable> soup = new Soup<Carrot>();
soup.add(new Tomato());
Nach dem Login kopieren
Der erste Satz ist in Ordnung, Suppe ist eine Unterklasse von Suppe kann der Variablen Suppe die Instanz von Soup zuweisen. Der zweite Satz ist kein Problem, da Suppe als Soup deklariert ist und ihre Add-Methode einen Parameter vom Gemüsetyp erhält und Tomato Gemüse ist und den richtigen Typ hat.
Allerdings gibt es ein Problem, wenn die beiden Sätze zusammengesetzt werden. Die eigentliche Suppensorte ist Soup, und wir haben eine Instanz von Tomato an die Add-Methode übergeben! Mit anderen Worten: Wenn wir Karottensuppe mit Tomaten zubereiten, werden wir sie definitiv nicht zubereiten können. Obwohl es logisch ist, Soup als Unterklasse von Soup zu behandeln, ist dies bei der Verwendung fehlerhaft.
Was ist also die Beziehung zwischen Suppe und Suppe? Unterschiedliche Sprachen haben unterschiedliche Verständnisse und Implementierungen. Zusammenfassend gibt es drei Situationen.
(1) Wenn Soup eine Unterklasse von Soup ist, wird die generische Suppe als kovariante bezeichnet.
(2) Wenn Soup ; sind zwei nicht verwandte Klassen, dann heißt die generische Soup (3) Wenn Soup die übergeordnete Klasse von Soup das Gegenteil Geändert. (Aber Kontravarianz ist nicht üblich)
Verstehen Sie die Konzepte von Kovarianz, Invarianz und Kontravarianz und schauen Sie sich dann die Implementierung in Java an. Die allgemeinen Generika von Java sind unveränderlich, was bedeutet, dass Soup
zwei nicht verwandte Klassen sind und Instanzen einer Klasse nicht Variablen der anderen Klasse zugewiesen werden können. Daher kann der obige Code, der Tomaten zur Herstellung von Karottensuppe verwendet, überhaupt nicht kompiliert werden. 2. Array-Kovarianz
In Java sind Arrays Grundtypen, keine Generika, und es gibt kein Array . Aber es ist einem generischen Typ sehr ähnlich, da es sich um einen Typ handelt, der aus einem anderen Typ erstellt wurde. Daher müssen Arrays auch als veränderbar betrachtet werden. Im Gegensatz zur Unveränderlichkeit von Generika sind Java-Arrays kovariant. Mit anderen Worten, Carrot[] ist eine Unterklasse von Gemüse[]. Die Beispiele im vorherigen Abschnitt haben gezeigt, dass Kovarianz manchmal Probleme verursachen kann. Zum Beispiel der folgende Code
Vegetable[] vegetables = new Carrot[10];
vegetables[0] = new Tomato(); // 运行期错误
Nach dem Login kopieren
Da Arrays kovariant sind, ermöglicht der Compiler die Zuweisung von Carrot[10] zu Variablen vom Typ Gemüse[], also dies Der Code kann erfolgreich kompiliert werden. Erst zur Laufzeit, wenn die JVM tatsächlich versucht, eine Tomate in einen Karottenhaufen zu stecken, geht etwas Großes schief. Daher löst der obige Code zur Laufzeit eine Ausnahme vom Typ java.lang.ArrayStoreException aus. Array-Kovarianz ist eines der berühmtesten historischen Probleme Javas. Seien Sie vorsichtig bei der Verwendung von Arrays! Wenn Sie das Array im Beispiel durch eine Liste ersetzen, sieht die Situation anders aus. So ArrayList<Vegetable> vegetables = new ArrayList<Carrot>(); // 编译期错误
vegetables.add(new Tomato());
Nach dem Login kopieren
ArrayList ist eine generische Klasse und unveränderlich. Daher besteht keine Vererbungsbeziehung zwischen ArrayList und ArrayList und dieser Code meldet einen Fehler während der Kompilierung. Obwohl beide Codeteile Fehler melden, sind Kompilierzeitfehler normalerweise einfacher zu behandeln als Laufzeitfehler. 3. Wenn Generika auch Kovarianz und Kontravarianz wollen
Generika sind unveränderlich, aber in einigen Szenarien hoffen wir immer noch, dass es kovariieren kann. Es gibt zum Beispiel eine junge Dame, die jeden Tag Gemüsesuppe trinkt, um abzunehmenclass Girl {
public void drink(Soup<Vegetable> soup) {}
}
Nach dem Login kopieren
我们希望drink方法可以接受各种不同的蔬菜汤,包括Soup和Soup。但受到不变性的限制,它们无法作为drink的参数。
要实现这一点,应该采用一种类似于协变性的写法
public void drink(Soup<? extends Vegetable> soup) {}
Nach dem Login kopieren
意思是,参数soup的类型是泛型类Soup,而T是Vegetable的子类(也包括Vegetable自己)。这时,小姐姐终于可以愉快地喝上胡萝卜汤和西红柿汤了。
但是,这种方法有一个限制。编译器只知道泛型参数是Vegetable的子类,却不知道它具体是什么。所以,所有非null的泛型类型参数均被视为不安全的。说起来很拗口,其实很简单。直接上代码
public void drink(Soup<? extends Vegetable> soup) {
soup.add(new Tomato()); // 错误
soup.add(null); // 正确}
Nach dem Login kopieren
方法内的第一句会在编译期报错。因为编译器只知道add方法的参数是Vegetable的子类,却不知道它具体是Carrot、Tomato、或者其他的什么类型。这时,传递一个具体类型的实例一律被视为不安全的。即使soup真的是Soup类型也不行,因为soup的具体类型信息是在运行期才能知道的,编译期并不知道。
但是方法内的第二句是正确的。因为参数是null,它可以是任何合法的类型。编译器认为它是安全的。
同样,也有一种类似于逆变的方法
public void drink(Soup<? super Vegetable> soup) {}
Nach dem Login kopieren
这时,Soup中的T必须是Vegetable的父类。
这种情况就不存在上面的限制了,下面的代码毫无问题
public void drink(Soup<? super Vegetable> soup) {
soup.add(new Tomato());
}
Nach dem Login kopieren
Tomato是Vegetable的子类,自然也是Vegetable父类的子类。所以,编译期就可以确定类型是安全的。
Das obige ist der detaillierte Inhalt vonEinführung in das Wissen über Java-Array-Kovarianz und generische Invarianz (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!