Subtypisierung ist invariant für parametrisierte Typen. Auch wenn die Klasse Dog
ist ein Subtyp von Animal
der parametrisierte Typ List<Dog>
ist kein Subtyp von List<Animal>
. Im Gegensatz dazu, Kovariante Subtypisierung wird von Arrays verwendet, so dass das Array Typ Dog[]
ist ein Subtyp von Animal[]
.
Die invariante Subtypisierung stellt sicher, dass die von Java erzwungenen Typbeschränkungen nicht verletzt werden. Betrachten Sie den folgenden Code von @Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
Wie von @Jon Skeet erwähnt, ist dieser Code illegal, da er sonst die Typbeschränkungen verletzen würde, indem er eine Katze zurückgibt, obwohl ein Hund erwartet wird.
Es ist aufschlussreich, den obigen Code mit dem analogen Code für Arrays zu vergleichen.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
Der Code ist legal. Er löst jedoch ein Ausnahme beim Speichern von Arrays . Ein Array trägt seinen Typ zur Laufzeit, so kann die JVM erzwingen Typsicherheit der kovarianten Subtypisierung erzwingen.
Um dies besser zu verstehen, schauen wir uns den Bytecode an, der von javap
der Klasse unten:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
Mit dem Befehl javap -c Demonstration
zeigt dies den folgenden Java-Bytecode:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Beachten Sie, dass der übersetzte Code der Methodenkörper identisch ist. Der Compiler ersetzte jeden parametrisierten Typ durch seinen Löschen . Diese Eigenschaft ist von entscheidender Bedeutung, da sie die Abwärtskompatibilität nicht beeinträchtigt.
Zusammenfassend lässt sich sagen, dass Laufzeitsicherheit für parametrisierte Typen nicht möglich ist, da der Compiler jeden parametrisierten Typ durch seine Löschung ersetzt. Damit sind parametrisierte Typen nichts weiter als syntaktischer Zucker.
22 Stimmen
Und eine völlig unabhängige Grammatikfrage, die mich gerade beschäftigt - sollte mein Titel "warum" lauten? sind nicht Javas Generika" oder "Warum ist nicht Java's generics"? Ist "Generika" Plural wegen des s oder Singular, weil es eine Einheit ist?
28 Stimmen
Generika, wie sie in Java verwendet werden, sind eine sehr schlechte Form des parametrischen Polymorphismus. Setzen Sie nicht zu viel Vertrauen in sie (wie ich es früher getan habe), denn eines Tages werden Sie hart an ihre erbärmlichen Grenzen stoßen: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Macht nicht berechnen [TM]. Das ist die Einschränkung der Java-Generik. Jede OOA/OOD kann gut in Java übersetzt werden (und MI kann sehr schön mit Java-Schnittstellen gemacht werden), aber Generika sind einfach nicht geeignet. Sie sind gut für "Sammlungen" und prozedurale Programmierung, dass sagte (das ist, was die meisten Java-Programmierer sowieso tun, so...).
8 Stimmen
Die Superklasse von List<Dog> ist nicht List<Animal>, sondern List<?> (d. h. eine Liste unbekannten Typs). Generics löscht Typinformationen im kompilierten Code. Dies geschieht, damit Code, der Generics verwendet (Java 5 und höher), mit früheren Versionen von Java ohne Generics kompatibel ist.
5 Stimmen
Verwandte SO-Fragen - Was nützt es, <? extends SomeObject> statt <SomeObject> zu sagen?
0 Stimmen
Ich möchte nur hinzufügen, dass es ein offizielles Java-Tutorial zu diesem Thema gibt: docs.oracle.com/javase/tutorial/extra/generics/subtype.html
10 Stimmen
@froadie, da niemand zu antworten schien... es sollte definitiv heißen "warum sind Javas Generics nicht...". Das andere Problem ist, dass "generisch" eigentlich ein Adjektiv ist und "Generika" sich auf ein fallengelassenes Pluralnomen bezieht, das durch "generisch" modifiziert wird. Man könnte sagen "diese Funktion ist eine generische", aber das wäre umständlicher als "diese Funktion ist generisch". Allerdings ist es auch etwas umständlich, zu sagen "Java hat generische Funktionen und Klassen", anstatt einfach "Java hat Generika". Als jemand, der seine Magisterarbeit über Adjektive geschrieben hat, denke ich, dass Sie über eine sehr interessante Frage gestolpert sind!
1 Stimmen
List<A>
hat keine Beziehung zuList<B>
unabhängig von der Beziehung zwischen den Klassen A und B.0 Stimmen
@dantiston Generics könnte als kollektiver Singular betrachtet werden, in diesem Fall funktioniert "isn't" sehr gut.
0 Stimmen
@SyntaxT3rr0r In Ihrem Beispiel würde meine Lösung darin bestehen, eine Superschnittstelle von
Scalpel
ySponge
(wahrscheinlich mit einem Namen wieSurgeonItem
), dann deklariertSurgeon extends Handable<SurgeonItem>
.1 Stimmen
@rai.skumar
List<?>
ist nicht der einzige wirksame Supertyp vonList<Dog>
. Andere Beispiele sindList<? extends Dog>
yList<? super Dog>
. Außerdem kann jede Variante der vier (<Dog>
,<? super Dog>
,<? extends Dog>
,<?>
), angewandt auf einen Supertyp vonList
gelten ebenfalls:Collection<Dog>
,Iterable<? super Dog>
, usw.. Überall, wo Sie einen Verweis auf eines dieser Elemente haben, können Sie auch eineList<Dog>
. AuchList<? extends Animal>
yList<? super Chihuahua>
.1 Stimmen
Verwandt: Was ist PECS (Producer Extends Consumer Super)?