Generics:Typverträglichkeit bei <? super Integer>

Steve222

Mitglied
Hallo!

Hoffentlich ist diese Frage genau hier "erlaubt".
Da es um Datenstrukturen geht, denke ich, ist es wohl korrekt.

Gegeben sind zwei Listen:

Java:
         List<Number> list14 = null;
         List<? super Integer> list15 = null;  

         //list14 = list15; //  Compilerfehler incompatible types   //Zeile 3

         list15 = list14;  //  Zeile 4,  OK
       
         // <? super Integer> bedeutet:  <Integer> und alle Super-Klassen von <Integer>

Kann mir jemand erklären, warum die Zuweisung in Zeile 4 in Ordnung ist?
Im konkreten Fall könnte da eine Typ-Zuweisung "<Integer> = <Number>" sein,
was, denke ich, nicht passt.

Kann man es so deuten, dass der Compiler großzügig bzw. wohlwollend entscheidet,
weil bei Zuweisung "<? super Integer> = <Number>" könnte auch
Typ-Zuweisung "<Number> = <Number>" sein oder auch
Typ-Zuweisung "<Object> = <Number>"
sein, was ja passt.

Viele Grüße

Steve222
 
Hallo,

schau mal hier:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ101
http://www.tutorials.de/java/367497-wieder-einmal-probleme-mit-java-generics.html

Schau mal hier:
Java:
package de.tutorials;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class SuperGenericsExample {
	public static void main(String[] args) {
		List<Number> numbers = Arrays.<Number>asList(0.0); //Double
		List<? super Integer> superOfIntegers = numbers;
		
		Number number = numbers.get(0); //returns number
		Iterator<Number> numberIterator = numbers.iterator();
		
		Object value = superOfIntegers.get(0); //returns Object NOT Integer ...
		superOfIntegers.set(0, /* accepts only integers*/ Integer.valueOf(1));
		Iterator<? super Integer> superIntegerIterator = superOfIntegers.iterator();
		superOfIntegers.add(/* accepts only integers*/ Integer.valueOf(0));
		
	}
}
Die Deklaration von List<? super Integer> superOfIntegers; besagt, dass superOfIntegers zwar Werte vom Typ Integer oder seiner Basis-Klassen enthalten kann, jedoch hier das "Constraint" ? super Integer unterschiedliche Auswirkungen. Zum einen ist superOfIntegers.set(...) fest auf Integer getyped.... man kann also diese Liste nur um tatsächliche Integers erweitern bzw. mit Integern manipulieren...
Auslesen kann man jedoch nur den "lower bound" ... und der ist in unserem Fall durch das ? -> Object.
Deshalb gibt die get Methode nur Object zurück.

Dieser Mechanismus macht sich das PECS-Prinzip zu nutze siehe in den oberen Links für weitere Einzelheiten.


Gruß Tom
 
Hallo Tom,

Du schriebst u.a.:
Auslesen kann man jedoch nur den "lower bound" ... und der ist in unserem Fall durch das ? -> Object.
und das irritiert mich.

? super SubType " (wildcard with lower bound), bedeutet
alle SuperTypen von SubType und SubType selbst.
Laut Fr.A.Langer ist <? super Integer> eine "wildcard with lower bound" und der lower bound
ist dann enstprechend "Integer". Oder?

Es kommt mir vor, als würdest Du unter "lower bound" den SuperTyp verstehen!?

Viele Grüße
Steve222
 
Hallo,

okay, kann sein, dass ich da was durcheinander gebracht habe. Finde die Formulierungen ziemlich kompliziert... für mich war der lower Bound der Typ den ich mindestens bekomme beim auslesen...
Was ist denn genau der upper bzw. lower bound einer generischen Collection? Ist der lower bound der allgemeinste Typ mit dem ich mit der Collection arbeiten kann? Was bedeutet arbeiten in diesem Fall? Lesen? Schreiben? Beides? Weil ...
List<? super Integer> superOfIntegers -> schreiben kann ich nur Integer ... lesen kann ich jedoch Object (da ja alle möglichen SuperTypen von Integer)zurück kommen können...

Im Prinzip definiert man ja mit List<? super T> eine Einschränkung für die Verwendung der Liste.
Diese Einschränkung besagt, dass zur Liste nur Werte vom Typ T hinzgefügt (add(...) ) und nur Werte vom Typ T gesetzt (set(int,...) werden können. Ausgelesen werden können jedoch Werte vom beliebigen Super-Typen von T und da bleibt nur Object über...

Deshalb ist die Zuweisung im Falle von:
Java:
List<Number> numbers = Arrays.<Number>asList(0.0); //Double
List<? super Integer> superOfIntegers = numbers;

auch erlaubt... denn ausgelesen werden kann aus superOfIntegers nur Werte vom Typ Object und dass sind Numbers oder Integer auf jeden Fall.

Aber wie heißts noch so schön: "Wer meint generics zu verstehen hat generics nicht verstanden...." :)

Gruß Tom
 
Hallo Tom,

gut, dass Du pragmatisch daran gehst und Lesen und Schreiben ausprobierst, aber dennoch sollten die Begrifflichkeiten geklärt werden.

Du fragtest
Was ist denn genau der upper bzw. lower bound einer generischen Collection? Ist der lower bound der allgemeinste Typ mit dem ich mit der Collection arbeiten kann?

Diese Begriffe sind die, die Frau Langer verwendet und ich verstehe sie so:
1.) lower bound ( ca.: untere Begrenzung) und auch upper bound ( ca.: obere Begrenzung)
gibt es so erstmal NUR im Zusammenhang mit dem wildcard-Zeichen "?", wenn es um
Typisierungen von Collections, also dem Thema "generics" geht.
Wie Du mitteilst geht es darum jeweils Collection-Objekte auf auf einen bestimmten
Typ, oder auf einige wenige Typen festzulegen.

Ohne wildcard, also z.B. <String>, bedeutet, dass in dem so markierten Collectionobjekt nur
Strings hinzugefügt werden dürfen.
Mit wildcard, also z.B. <? extends String> bedeutet, dass in das so markierte Collectionobjekt nur
Strings UND Subclass-Objekte von String hinzugefügt werden dürfen. "String" ist dann "upper bound".

Mit wildcard, also z.B. <? super String> bedeutet, dass in das so markierte Collectionobjekt nur
Strings UND Superclass-Objekte von String hinzugefügt werden dürfen. "String" ist dann "lower bound".

Was bedeutet arbeiten in diesem Fall? Lesen? Schreiben?
Um das Lesen mit get() habe ich mich bisher kaum gekümmert, aber eben habe ich in der
API nachgeschaut und kann nicht bestätigen, dass get() Object zurückgibt
Da steht bei List "E get(int index)" wobei E als Typparameter der List erklärt wird und
der wäre meines Erachtens eben das was innerhalb < > steht.

List<? super Integer> superOfIntegers -> schreiben kann ich nur Integer ... lesen kann ich jedoch Object (da ja alle möglichen SuperTypen von Integer)zurück kommen können...
Ja, so wie ich es verstanden alle möglichen SuperTypen von Integer also auch Object.

Nun ja, mein anfängliches Problem ist noch nicht gelöst. :(
Ich bin über die Seite von Nikos P.

//http://nikojava.wordpress.com/2008/10/09/scjp-mock-exam-for-generics/#scjp-generics-25
zu der anfänglichen Frage gestoßen.

List<Number> list10 = null;
List<Integer> list11 = null;
list11 = list10; // Compiler-Fehler incompatible types
list10 = list11; // Compiler-Fehler incompatible types ==> ES GILT OFFENBAR KEINE IS-A BEZIEHUNG !

Wegen dem vorgenanten schließe ich, dass IS-A Relation bei generics keine Rolle spielt.
Ich denke, dass
Java:
List<Number> list14 = null;
         List<? super Integer> list15 = null; 
         list15 = list14;  //  Zeile 4,  OK
OK ist, ist weil eine der drei Möglichkeiten genau passt, da die IS-A Relation offenbar bei generics keine Rolle spielt. Das meine ich auch mit
Compiler großzügig bzw. wohlwollend entscheidet

Aber bei
Java:
 Queue<? extends Number> q1 = null;
 Queue<? super Integer> q2 = null;
q1 = q2  //  Compiler-Fehler:  incompatible types
gäbe es auch mindestens eine Kombination die OK ist, aber es gibt hier auch den Compiler-Fehler "incompatible types..." womit die Theorie "großzügige Entscheidung" ( soll heissen: wenn wenigstens eins passt, dann kein Compiler-Fehler )
auch nicht stimmen kann!
Deshalb bin ich erstmal immer noch ratlos.


Viele Grüße
Steve222
 
@steve222
Verstehe ich das richtig? <? super Integer> darf super typen aufnehmen.

und du willst eine Collection vom Typ Number in diese Collection vom Typ <? super Integer> schreiben.

Number ist doch super typ von Integer. Oder verstehe ich hier gerade alles falsch?
 
Hallo,

gut, dass Du pragmatisch daran gehst und Lesen und Schreiben ausprobierst, aber dennoch sollten die Begrifflichkeiten geklärt werden.
da hast du natürlich recht.

Wie Du mitteilst geht es darum jeweils Collection-Objekte auf auf einen bestimmten
Typ, oder auf einige wenige Typen festzulegen.
Genau.

Um das Lesen mit get() habe ich mich bisher kaum gekümmert...
Das sollte man aber nicht außen vor lassen, denn Collections werden IMHO deutlich öfter gelesen als geschrieben.

... aber eben habe ich in der
API nachgeschaut und kann nicht bestätigen, dass get() Object zurückgibt
Da steht bei List "E get(int index)" wobei E als Typparameter der List erklärt wird und
der wäre meines Erachtens eben das was innerhalb < > steht.

Ja genau und was ist dieses E in dem Szenario?
Richtig: ? super Integer und welchen Typ haben wir dann? -> (? super Integer) und das wäre dann
allgemein: Object. Etwas anderes kannst du dann nämlich nicht definieren. Das gäbe dann einen
Compilefehler.
Java:
List<? super Integer> superOfIntegers = ...
Object value = superOfIntegers.get(0);

Hier liegt wie schon beschrieben ein wichtiger Unterschied:
Java:
List<? super Integer> list = null;
list.add(new Integer(0)); //list.add(E e) -> hier wird nur Integer akzeptiert
Object integer = list.get(0); //E list.get(int) -> hier wird nur Object garantiert!

Java Generics sind nicht Covariant... sprich eine List<Integer> ist keine List<Number>, noch nichtmal eine List<Object>
Siehe auch:
http://www.fh-wedel.de/~si/seminare/ws05/Ausarbeitung/5.generics/genjava3.htm

Java:
List<Integer> ints = null;
List<Object> os = ints; //Compiler error

Mit Wildcards kann man diesen Umstand etwas auflösen.
Java:
List<Integer> ints = null;
List<? extends Object> os = ints; //ok

Java:
Queue<? extends Number> q1 = null;
Queue<? super Integer> q2 = null;
q1 = q2  //  Compiler Fehler

Hier mal noch zwei Beispiele zu deinem Queue-Fall:
Java:
Queue<? extends Number> q1 = null; //q1 garantiert numbers -> Number peek(), akzeptiert: ****? 
Queue<? super Integer> q2 = null; //q2 garantiert objects -> Object peer(), akzeptiert: Integer
q1 = q2  //  geht nicht: etwas was Objects garantiert kann nicht plötzlich Numbers garantiert... deshalb Compilerfehler
q2 = q1; // geht auch nicht

Was gehen würde:
Java:
Queue<Number> q1 = null; //q1 garantiert numbers -> Number peek(), akzeptiert: Number void add(Number)
Queue<? super Integer> q2 = null; //q2 garantiert objects -> Object peer(), akzeptiert: Integer
//q1 = q2  //  geht nicht: etwas was Objects garantiert kann nicht plötzlich Numbers garantiert... deshalb Compilerfehler
q2 = q1; // geht

Gruß Tom
 
Eine Liste wie list<? super Integer> nimmt Integer, Number und Object auf. Warum? Weil Object super Klasse von Number und Number super Klasse von Integer ist. Und <? super Integer> besagt das die Liste Integer und dessen super Klassen aufnimmt.
Und solange rechts etwas steht was nur einem Kriterium erfüllt funktioniert die Zuweisung.

Während list<Integer> besagt das nur Vollwertige Integer aufgenommen werden.

Was bedeutet in unserem Fall Vollwertig? Vollwertig bedeutet das das der Typ der aufgenommen wird exakt mit den Vorgaben Übereinstimmen muss. Integer IS-A Number, aber Number IS-Not A Integer. und weil Number kein Integer ist kann eine Zuweisung von list<integer> = list<Number> nicht funktionieren.

Ich versuche es mal zu verdeutlichen

die Prüfung des Compilers bei <? super Integer> ist ob die liste die gegeben wird einem der super Typen entspricht
also Number || Integer || Object, solange nur eins true ergibt wird die gegebene Liste Akzeptiert.

die Prüfung bei list<Integer> verlangt nach Integer und Prüft nur das ab und solange es false ergibt wird nichts angenommen.
 
Hallo Tom,

Du schriebst:
Ja genau und was ist dieses E in dem Szenario?
Richtig: ? super Integer und welchen Typ haben wir dann? -> (? super Integer) und das wäre dann
allgemein: Object. Etwas anderes kannst du dann nämlich nicht definieren. Das gäbe dann einen
Compilefehler.
[ :( Auch wenn ich wohl weniger weiss bzgl. Java als Du... ]

genau die Stelle:
...das wäre dann allgemein: Object...
Und genau das habe ich bisher anders aufgefasst:
<? super Integer> steht, denke ich, für Integer oder Number oder Object.
Also "Integer" ist hier lower bound und somit sind Integer (!) und sene Superklassen
als Typ erlaubt.


Im Moment hilft mir das alles zu meiner anfänglichen Frage nicht wirklich weiter, denn es sind einige
Wiederholungen und auch Fakten, die mir bereits seit längerem belkannt sind.

wie z.B.
Java Generics sind nicht Covariant... sprich eine List<Integer> ist keine List<Number>, noch nichtmal eine

List<Object> Ich schrieb, dass IS-A Relationen bei generics keine Rolle spielen.

Was mir hoffentlich was nutzt, ist die Anregung zum rumpropieren mit Lesen und Schreiben.
Aber da bin ich im Moment auf etliches gestoßen was ich einfach nicht einordnen kann
wie z.B.
Java:
  Queue<? extends Number> q1 = null;
         boolean boo1 = q1.offer(new Integer(88));  // C-FM capture ..  
         boolean boo2 = q1.add(new Integer(99));   // : cannot find symbol

Für mich bedeutet Queue<? extends Number> q1 = null; dass in q1
Integer-Objekte hinzugefügt werden können, weil ...Integer extends Number..
Integer IS-A Number
upper bound ist hier Number aber Number-Objekte gehen nicht, weil es die garnicht, weil abstract,
gibt.

Das was Du am Ende schreibst ist prinzipiell das gleiche (jetzt nur mit queue anstatt List) was ich ganz am Anfang geschrieben habe, oder nicht? Ich weiß nicht genau wie Du Dich selbst einschätzt bezüglich generics Es gibt da divergente Hinweise...Sofern Du bei generics Lücken hast kannst Du diese aufgrund Deiner Erfahrung bestimmt recht bald schließen.

Ich habe vor IMMER zu verstehen, warum ein Compilerfehler auftritt.
Es nur in den meisten Fällen zu wissen, reicht mir nicht.

Die "wildcards with an upper bzw. lower bound" geben mir gerade in der praktischen
Verwendung (Lesen, Schreiben) im Moment noch mehr Rätsel auf.

Viele Grüße
Steve222
 

Neue Beiträge

Zurück