Frage zu Generics: Unterschied zwischen "Object" und "? extends Object"?

DarthShader

Erfahrenes Mitglied
Hallo zusammen,

ich habe eine Frage zu Java Generics.

Folgendes funktioniert:

Java:
List< Object > objects = new ArrayList< Object >();
objects.add( new String( "123" ) );

Dieses jedoch nicht:

Java:
List< ? extends Object > objects = new ArrayList< Object >();
objects.add( new String( "123" ) );

Ich dachte, "? extends Object" würde bedeuten, dass es eine Liste von Objekten ist, die von "Object" ableiten? Also sprichwörtlich "irgendwas, was Object erweitert"?

Allerdings kompiliert das zweite Beispiel nicht - wo ist der Unterschied?

(Das Beispiel hat natürlich keinen wirklichen Sinn, es soll nur exemplarisch den Unterschied "kompiliert" und "kompiliert nicht" darstellen.)


Über Eure Hilfe würde ich mich sehr freuen


Vielen Dank!
 
Wozu?
Fast alle Objekte im Java sind von Object abgeleitet (mal von den Standartdatentypen abgesehen) also reicht es wenn du schreibst List<Object> denn da passen sowieso (fast) alle Objekte rein.

Warum sich das nun nicht kompilieren lässt fänd ich auch mal ganz interessant.
 
Hallo Unicate,

vielen Dank für Deine Antwort.

Das ist nicht böse gemeint, aber exakt so eine Antwort wollte ich mit dem Hinweis, dass dies nur ein Beispiel ist, vermeiden.

Es geht mir eben nicht darum, um das praxisnah ist, oder man es so machen sollte. Ich weiß natürlich, dass alle komplexen Typen in Java von "Object" ableiten und dass man gegen Interfaces programmieren sollte sowie dort dann Polymorphie ausnutzen kann.

Ich würde jedoch gerne verstehen, wo der Unterschied ist - warum das erste kompiliert, das zweite jedoch nicht, also was die Hintergründe davon sind.
 
Hi.
Ich würde jedoch gerne verstehen, wo der Unterschied ist - warum das erste kompiliert, das zweite jedoch nicht, also was die Hintergründe davon sind.
Das ? extends Object ist eine Zusicherung, dass in der Liste nur von Object abgeleitet Instanzen drin sind. Soweit logisch.

Es könnte sich in einem konkreten Fall also um eine List<MeinSpezialObjekt> handeln falls MeinSpezialObjekt von Object abgeleitet wurde. Auch sofort logisch, dass man in eine solche Liste nicht einfach ein String Objekt einfügen kann.

Ein extend Wildcard ist sinnvoll wenn man aus dem generischen Container bestimmte Attribute / Methoden verwenden will.

Gruß
 
Hallo deepthroat,

danke für Deine Antwort.

Leider habe ich es noch nicht verstanden.

Das ? extends Object ist eine Zusicherung, dass in der Liste nur von Object abgeleitet Instanzen drin sind. Soweit logisch.

Das "List< Object >" ist doch auch eine Zusicherung, dass sich entweder "Object" oder von "Object"-abgeleitete Objekte in der Liste befinden, was ein "list.add( "string" )" ja zeigt ("String" ist von "Object" abgeleitet).

Was genau verstehe ich hier nicht? :)
 
was man hier mal zur abwechslung fragen sollte

welche fehlermeldung wirft der compiler beim zweiten beispiel

du sagst überall : das zweite bespiel compiliert nicht ...
gut ... und WARUM ? ... es wäre bestimmt sehr hilfreich wenn du uns mal die compile-time-exception posten würdest anhand derer wir dann rätzeln könnten warum sie geworfen wird ...
einfach mit dem text : compiliert nicht kann niemnad was anfangen ...

*schade das diese frage bis jetzt nicht gestellt wurde*
 
Hallo,

sorry, ich dachte, wenn sich jemand mit den Generics auskennt, der sieht quasi auf den ersten Blick, was an dem unteren Zweizeiler nicht stimmt.

Hier nochmal das Beispiel, was nicht kompiliert:

Java:
List< ? extends Object > objects = new ArrayList< Object >();
objects.add( new String( "123" ) );

Der Compiler merkt folgendes an, Eclipse unterstreicht dabei das "add":

Code:
The method add(capture#1-of ? extends Object) in the type 
List<capture#1-of ? extends Object> is not applicable for the arguments (String)

Danke!
 
Covariante Typen sind gegen Schreibzugriffe gesperrt, mit Ausnahme von NULL.

Folgendes ist also das einzige, was du mit der add-Methode machen kannst.

Java:
List< ? extends Object > objects = new ArrayList< Object >();
objects.add(null);


Das liegt einfach daran, dass du nicht wissen kannst, welchen Typ die Werte in 'objects' tatsächlich haben. Lesen geht aber, da du ja weißt, dass alle Werte die du ließt auf jeden Fall zum Typ kompatibel sind.


Natürlich macht das in deinem konkreten Fall keinen Sinn, weil du nur Object hast, aber das interessiert den Compiler nicht.


Java:
List< ? extends Number > objects = new ArrayList< Integer >();

//Schreiben darf nicht zugelassen werden. Denn der Compiler weiß ja nicht, welcher Typ tatsächlich drin steckt
//objects.add(new Float(1.5));

//Lesen geht, weil egal was drin steckt, es ist zu Number kompatibel
Number n = objects.get(0);

Dazu sollte man vielleicht noch wissen, dass Generics ausschließlich den Compiler/Präprozessor interessieren. Im kompilierten Code ist alles durch Casts ersetzt! Diese Casts können natürlich dann vom Präprozessor gefahrlos eingefügt werden, weil durch die Regeln der Generics sichergestellt ist, dass Sie auch funktionieren. Für nichts anderes sind Generics gut. Folgendes ist also äquivalent.

Java:
List<String> l = new ArrayList<String>();
l.add("Foo");
String s = l.get(0);

//Absolut Identisch
List l = new ArrayList();
l.add("Foo");
String s = (String)l.get(0);
 
Hallo CPoly,

vielen Dank für Deine Antwort - ja, das macht jetzt absolut Sinn für mich. Ich denke ich habe es verstanden. Das Fragezeichen "?" im Kovarianten-Typ, also der generische Parameter, gibt also "irgendwas an", damit kann der Compiler nicht entscheiden, ob das, was ich reinsetzen will, überhaupt erlaubt ist. Da es aber z.B. "extends Xyz" heißt, kann ich alles, was von Xyz oder deren Super-Klassen definiert wurde, natürlich lesen.

Danke für die Aufklärung - was mir jetzt noch zum letztendlichen Verständnis fehlt, ist ein sinnvolles Beispiel, wo man "? extends XYZ" verwendet, kannst Du mir da was kleines nennen?

Vielen Dank!
 
Ein Beispiel aus der Vorlesungsfolie wäre die generische Klasse für den Knoten eines Baumes/Graphen.
Die Methode copyFrom kann den Inhalt eines Knotens in den eigenen kopieren, auch wenn dieser nicht den exakt gleichen Typ hat.

Java:
class Node<T> {
    private T info;
    //...
    void copyFrom( Node<? extends T> other ) {
        info = other.getInfo( );
    }
}

Verwendung

Java:
Node<Number> n;
Node<Integer> i;
//...
n.copyFrom(i); // ok
i.copyFrom(n); // Fehler
 
Zurück