2014-03-03 29 views
22

Betrachten Sie die folgende Methode doSomething(List<Object>), die List<Object> als Parameter akzeptiert.Warum Liste <String> ist nicht akzeptabel als Liste <Object>?

private void doSomething(List<Object> list) { 
    // do something 
} 

nun unter Code-Schnipsel betrachten, die doSomething() zu nennen versucht, wo ich versuche List<String> zu doSomething()

List<Object> objectList; 
List<String> stringList; 

doSomething(stringList); // compilation error incompatible types 
doSomething(objectList); // works fine 

Code unten wirft

Kompilierungsfehler Auch passieren
objectList = stringList; // compilation error incompatible types 

Meine Frage ist, warum List<String> kann nicht an eine Methode übergeben werden, die List<Object> akzeptiert?

+3

Check out this: http://docs.oracle.com/javase/tutorial/extra/generics/subtype.html –

+6

'List 'soll das funktionieren lassen – AbstractChaos

+5

@AbstractChaos true, solange" do something "nicht mit" List # add (...) ' – Marco13

Antwort

8

Diese allgemeine Frage in Java kann einen Blick verwirrend, die nicht sehr vertraut mit Generics als in dem ersten Blick ist es wie String aussieht, ist Gegenstand so kann List<String> verwendet werden, wo List<Object> erforderlich ist, aber das ist nicht wahr. Dies führt zu einem Kompilierungsfehler.

Es macht Sinn, wenn Sie einen Schritt weiter gehen, weil List<Object>alles speichern kann einschließlich String, Integer usw., aber List<String> kann Strings nur speichern.

haben auch einen Blick auf: Why not inherit from List<T>?

+3

als Antwort ausgewählt wird, da es mich besser verstehen lässt. – user3374518

+5

Das Problem ist nicht genau, dass der Angerufene irgendetwas speichern kann: Das wirkliche Problem ist, wenn es erlaubt wäre, wäre der Anrufer nicht sicher, eine String-Instanz zu haben, also müsste er damit umgehen wie in den Vorgenerika-Tagen Operator). – robermann

31

Denn während StringObject erstreckt, List<String> nicht List<Object> erstreckt

Update:
Im Allgemeinen, wenn Foo ist ein Subtyp (Unterklasse oder Subschnittstelle) von Bar und G ist eine generische Typdeklaration. Es ist nicht der Fall, dass G<Foo> ein Subtyp von G<Bar> ist.

Dies liegt daran, Sammlungen ändern sich. In Ihrem Fall, wenn List<String> war ein Subtyp von List<Object>, dann anderen Typen als String kann hinzugefügt werden, wenn die Liste verwiesen wird, dessen übergeordneten Typen verwendet wird, wie folgt:

List<String> stringList = new ArrayList<String>; 
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object> 
objectList.add(new Object()); 
String s = stringList.get(0);// attempt to assign an Object to a String :O 

und die Java-Compiler, diese Fälle zu verhindern, haben .

Weitere Informationen auf this Java Tutorial Seite.

+0

Einfach und genau. +1 –

+1

+1; trotzdem kann man doSomething aufrufen ((Liste ) (Liste ) stringList)) – robermann

+4

Während diese Antwort wahr ist, sieht es mehr wie Bestätigung der OP Frage aus. IMHO fehlt es am wichtigsten Teil: "Warum' List 'ist kein Untertyp von' List '?". – Pshemo

15

Sie ein Objekt eines falschen Typs in die Liste setzen könnte IF dies gearbeitet:

private void doSomething(List<Object> list) { 
    list.add(new Integer(123)); // Should be fine, it's an object 
} 

List<String> stringList = new ArrayList<String>(); 
doSomething(stringList); // If this worked.... 
String s = stringList.get(0); // ... you'd receive a ClassCastException here 
+0

+1, vielleicht das beste Beispiel hier :) – robermann

+2

PS: Die Supertype/Subtyp Beziehungen mit Generika sind sehr gut unter http: // www. angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ207 – Marco13

0

Wenn Sie nicht sicher sind, welche Datentypen es in Sie nimmt Verwendung von Generics in Java machen kann wie folgt

public static void doSomething(List<?> data) { 

} 

public static void main(String [] args) { 
    List<Object> objectList = new ArrayList<Object>(); 
    List<String> stringList = new ArrayList<String>(); 
    doSomething(objectList); 
    doSomething(stringList); 
} 

Aber während die Daten verwenden, werden Sie aufgefordert richtigen Datentyp als Typ

+0

Keine Beziehung zu der Frage, denke ich –

+0

Ya, ich weiß, hat keine Beziehung zu Frage, aber da Sie richtigen Kommentar auf die Frage hinzugefügt hatte, dachte, es wäre gut zu Teilen möglich Generischer Weg mit Generics in Java: - / – rsakhale

3

Es somet Cast zu spezifizieren, Es wird erwartet, dass ein List<Object> ein Supertyp von List<String> wäre, weil Object ein Supertyp von String ist.

Diese Erwartung beruht auf der Tatsache, dass eine solche Art Beziehung für Arrays besteht:

Object[] ist ein übergeordneter Typ von String[], weil ein übergeordneter Typ von ObjectString ist. (Diese Typbeziehung wird als Kovarianz bezeichnet.)

Die Super-Subtyp-Beziehung der Komponententypen erstreckt sich auf die entsprechenden Array-Typen.

Es gibt keine solche Beziehung vom Typ für Instanziierungen generischer Typen. (Parameterized-Typen sind nicht covariant.)

prüfen here für weitere Details

2

Von Java Tutorials von Generics:

Lassen Sie uns Ihr Verständnis von Generika testen. Ist das folgende Code-Snippet legal?

List<String> ls = new ArrayList<String>(); // 1 
List<Object> lo = ls; // 2 

Linie 1 ist sicherlich legal. Der schwierigste Teil der Frage ist Zeile 2. Dies läuft auf die Frage hinaus: ist eine Liste von String eine Liste von Objekten. Die meisten Menschen antworten instinktiv: "Sicher!"

Nun, werfen Sie einen Blick auf die nächsten paar Zeilen:

lo.add(new Object()); // 3 
String s = ls.get(0); // 4: Attempts to assign an Object to a String! 

Hier haben wir aliased ls und lo. Wenn wir auf ls, eine Liste von String, über den Alias ​​lo zugreifen, können wir beliebige Objekte in sie einfügen. Infolgedessen hält Ls nicht mehr nur Strings, und wenn wir versuchen, etwas daraus zu machen, bekommen wir eine grobe Überraschung.

Der Java-Compiler wird dies natürlich verhindern. Zeile 2 verursacht einen Kompilierzeitfehler.

Quelle: Generics and Subtyping

4

Der Grund für diese Einschränkungen haben mit der Varianz Überlegungen zu tun.

Nehmen Sie den folgenden Code ein:

public void doSomething(List<Object> objects) 
{ 
    objects.add(new Object()); 
} 

Ihrem Beispiel erweitern, könnten Sie versuchen, Folgendes zu tun:

List<String> strings = new ArrayList<String>(); 
string.add("S1"); 

doSomething(strings); 

for (String s : strings) 
{ 
    System.out.println(s.length); 
} 

Hoffentlich ist es offensichtlich, warum diese brechen würde, wenn der Compiler den Code erlaubt zu sein kompiliert (was es nicht tut) - ein ClassCastException würde für das zweite Element in der Liste auftreten, wenn versucht wird, die Object zu String zu übertragen.

Um generaliSammlungsTypen passieren, müssen Sie dies tun:

public void doSomething(List<?> objects) 
{ 
    for (Object obj : objects) 
    { 
    System.out.println(obj.toString); 
    } 
} 

Auch der Compiler dem Rücken beobachtet und waren Sie die System.out mit objects.add(new Object()) der Compiler dies erlauben würde, nicht zu ersetzen weil objects könnte als List<String> erstellt worden sein.

Weitere Hintergrundinformationen über Variance siehe Wikipedia artical Covariance and contravariance

Verwandte Themen