Warum ist List kein Untertyp der List ?

StackOverflow https://stackoverflow.com/questions/1240316

Frage

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

Der Aufruf der Methode nicht geben Prüfung. Ich kann nicht einmal den Parameter werfen wie folgt:

wahey((List<Object>) new LinkedList<Number>());

Aus meiner Forschung habe ich gesammelt, dass der Grund für die nicht so dass dieser Typ-Sicherheit. Wenn wir die oben genannten tun durften, dann könnten wir folgende Voraussetzungen erfüllt sein:

List<Double> ld;
wahey(ld);

Innerhalb der Methode Wahey, konnten wir einige Strings in die Eingabeliste hinzuzufügen (wie die Parameter einen List<Object> Referenzhält). Jetzt, nach dem Methodenaufruf, ld bezieht sich auf eine Liste mit einer Art List<Double>, aber die aktuelle Liste enthält einige String-Objekte!

Dies scheint anders als die normale Art und Weise Java ohne Generika funktioniert. Zum Beispiel:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

Was wir hier tun, ist im Wesentlichen der gleiche, außer diese Kompilierung-Kontrollen passieren und nur zur Laufzeit fehlschlagen. Die Version mit Listen wird nicht kompiliert.

Dies führt mich zu glauben, dass dies eine reine Design-Entscheidung in Bezug auf die Art Einschränkungen für Generika. Ich habe gehofft, einige Kommentare zu dieser Entscheidung zu bekommen?

War es hilfreich?

Lösung

Was Sie in der „ohne Generika“ Beispiel tun, ist eine Besetzung, die deutlich macht, dass Sie etwas typ unsicher machen. Das Äquivalent mit Generika wäre:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

Welche verhält sich auf die gleiche Weise (kompiliert, aber nicht zur Laufzeit). Der Grund für das Verhalten nicht erlaubt über Sie fragen, weil es erlauben würde, implizite Typ-unsichere Aktionen, die viel schwerer sind in Code zu bemerken. Zum Beispiel:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

Das sieht völlig in Ordnung und typsicher, bis Sie es so nennen:

List<Double> list_d;
String s;

Foo(list_d, s);

Welche auch typsicher, da Sie als Anrufer sieht nicht unbedingt wissen, was Foo mit seinen Parametern tun werden.

Also in diesem Fall haben Sie zwei scheinbar typsicher Bit-Code, der zusammen typen unsicher am Ende wird. Das ist schlecht, weil sie versteckt sind und daher schwer zu vermeiden und schwerer zu debuggen.

Andere Tipps

Überlegen Sie, ob es war ...

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

Sie würden der Lage, etwas von der Muttertyp zu der Liste hinzufügen, die möglicherweise nicht, was es früher erklärt wurde, das als das obige Beispiel zeigt, alle möglichen Probleme verursacht. Somit ist es nicht erlaubt.

Sie sind nicht Subtypen voneinander durch, wie Generika Arbeit. Was Sie wollen, ist Ihre Funktion wie folgt zu erklären:

public void wahey(List<?> list) {}

Dann wird es eine Liste der alles akzeptieren, die Gegenstand erstreckt. Sie können auch tun:

public void wahey(List<? extends Number> list) {}

So können Sie in Listen von etwas nehmen, die eine Unterklasse von Nummer ist.

Ich würde empfehlen, eine Kopie von "Java Generics und Sammlungen" Pick up von Maurice Naftalin & Philip Wadler.

Es gibt im Wesentlichen zwei Dimensionen der Abstraktion hier die Liste Abstraktion und die Abstraktion seines Inhalts. Es ist völlig in Ordnung entlang der Liste Abstraktion variieren - zu sagen, zum Beispiel, dass es ein LinkedList oder eine Arraylist - aber es ist nicht in Ordnung weiter den Inhalt zu beschränken, zu sagen: Diese (Liste der Objekte enthält) ist eine (verkettete Liste der hält nur Zahlen). Da jede Referenz, die es als eine (Liste der Objekte enthält) weiß, versteht, durch den Vertrag seiner Art, dass es halten kann jeder Objekt.

Das ist ganz anders, als man in der Nicht-Generika-Beispiel-Code getan haben, wo Sie gesagt haben: diese String behandeln, als ob es sich um eine Doppel waren. Sie versuchen, anstatt zu sagen: diese (Liste, die nur Zahlen hält) zu behandeln als (Liste, die alles hält). Und dies nicht der Fall, und der Compiler kann es erkennen, so dass es nicht Sie nicht mit ihm durchgehen lassen.

  

"Was wir hier machen, ist im Wesentlichen   das gleiche, außer dies wird vorübergehen   Kompilierung-Kontrollen und nur scheitern an   Laufzeit. Die Version mit Listen nicht   kompilieren. "

Was Sie beobachten macht Sinn, wenn man bedenkt, dass der Hauptzweck der Java Generics ist, um Typ-Inkompatibilitäten versagen bei der Kompilierung statt Laufzeit zu bekommen.

java.sun.com

  

Generics bietet eine Möglichkeit für Sie   den Typ einer Sammlung kommunizieren   so an den Compiler, dass es sein kann,   geprüft. Sobald der Compiler kennt die   Elementart der Erhebung, die   Compiler können überprüfen, ob Sie verwendet haben   die Sammlung konsequent und kann   Fügen Sie die richtigen Würfe auf Werte   wobei aus der Sammlung genommen.

In Java List<S> nicht ein Subtyp von List<T> wenn S ein Subtyp von T ist. Diese Regel stellt Typsicherheit.

Nehmen wir an, wir zulassen, dass ein List<String> ein Subtyp von List<Object> sein. Betrachten Sie das folgende Beispiel:

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

Also, das zwingt bietet Sicherheit geben, aber es hat auch einen Nachteil, es ist weniger flexibel. Betrachten Sie das folgende Beispiel:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

Hier finden Sie eine Sammlung von T die haben, möchten Sie alle Artikel aus einer anderen Sammlung hinzuzufügen. Sie können diese Methode nicht aufrufen mit einem Collection<S> für einen S Subtyp von T. Idealerweise ist dies in Ordnung, weil Sie nur Elemente in Ihre Sammlung hinzugefügt werden, sind Sie nicht die Parametersammlung ändern.

Um dies zu beheben, bietet Java, was sie „Platzhalter“ nennen. Wildcards sind eine Möglichkeit, Kovarianz / Kontra bereitzustellen. Betrachten wir nun die folgenden mit Wildcards:

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

Jetzt, mit Wildcards wir Kovarianz in der Art T erlauben und wir blockieren Operationen, die nicht typsicher sind (beispielsweise ein Element in der Sammlung hinzufügen). Auf diese Weise erhalten wir die Flexibilität und Typsicherheit.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top