Das Problem wurde korrekt als mit der Abweichung zusammenhängend identifiziert, aber die Details sind nicht korrekt. Eine rein funktionale Liste ist ein kovarianter Datenfunktor, d. h. wenn ein Typ Sub ein Subtyp von Super ist, dann ist eine Liste von Sub definitiv ein Subtyp einer Liste von Super.
Die Veränderbarkeit einer Liste ist hier jedoch nicht das grundlegende Problem. Das Problem ist die Veränderbarkeit im Allgemeinen. Das Problem ist wohlbekannt und wird als Kovarianzproblem bezeichnet. Es wurde, glaube ich, zuerst von Castagna identifiziert und zerstört die Objektorientierung als allgemeines Paradigma vollständig und vollständig. Es basiert auf den zuvor etablierten Varianzregeln von Cardelli und Reynolds.
Betrachten wir - etwas vereinfachend - die Zuordnung eines Objekts B vom Typ T zu einem Objekt A vom Typ T als Mutation. Dies geschieht ohne Verlust an Allgemeinheit: eine Mutation von A kann als A = f (A) geschrieben werden, wobei f: T -> T. Das Problem ist natürlich, dass Funktionen zwar in ihrem Kodomain kovariant sind, aber in ihrem Domain kontravariant, aber bei Zuweisungen sind Domain und Kodomain dasselbe, also ist die Zuweisung invariant!
Daraus folgt verallgemeinernd, dass Subtypen nicht mutiert werden können. Bei der Objektorientierung ist die Mutation jedoch von grundlegender Bedeutung, so dass die Objektorientierung von Natur aus fehlerhaft ist.
Hier ein einfaches Beispiel: In einer rein funktionalen Umgebung ist eine symmetrische Matrix eindeutig eine Matrix, sie ist ein Untertyp, kein Problem. Fügen wir nun der Matrix die Fähigkeit hinzu, ein einzelnes Element an den Koordinaten (x,y) zu setzen, wobei sich kein anderes Element ändern darf. Jetzt ist die symmetrische Matrix kein Untertyp mehr, denn wenn Sie (x,y) ändern, haben Sie auch (y,x) geändert. Die funktionale Operation ist delta: Sym -> Mat, wenn Sie ein Element einer symmetrischen Matrix ändern, erhalten Sie eine allgemeine nicht-symmetrische Matrix zurück. Wenn Sie also eine Methode zum Ändern eines Elements in Mat aufgenommen haben, ist Sym kein Untertyp. In der Tat gibt es mit ziemlicher Sicherheit KEINE richtigen Untertypen.
Um es einfacher auszudrücken: Wenn Sie einen allgemeinen Datentyp mit einer Vielzahl von Mutatoren haben, die seine Allgemeinheit ausnutzen, können Sie sicher sein, dass ein geeigneter Untertyp unmöglich alle diese Mutationen unterstützen kann: Wenn er das könnte, wäre er genauso allgemein wie der Supertyp, was der Spezifikation des "geeigneten" Untertyps widerspricht.
Die Tatsache, dass Java die Subtypisierung von veränderbaren Listen verhindert, geht am eigentlichen Problem vorbei: Warum verwenden Sie objektorientierten Müll wie Java, wenn es schon vor mehreren Jahrzehnten in Verruf geraten ist?
Auf jeden Fall gibt es hier eine vernünftige Diskussion:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
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)?