"Kovariante Referenzen auf generische Typen"



  • http://www.c-plusplus.net/forum/viewtopic-var-t-is-103515.html

    Optimizer schrieb:

    Die haben leider viele interessante Sachen nicht geklaut, z.B. kovariante Referenzen auf generische Typen (List<? extends Foo>). Das hätt ich jetzt schon zwei mal gut brauchen können. 😞

    Dazu hätt ich eine Frage und zwar hab ich in meinem schlauen Büchlein
    gelesen, dass Wildcards nur lesenden Zugriff auf die Elemente erlauben,
    also eben sowas in der Richtung:

    public static void print(List<? extends Number> numbers)
    {
        for(Number n : numbers) {
            System.out.println(n.doubleValue());
        }
    }
    

    Aber bei schreibendem Zugriff gibts einen Fehler. Soweit so gut, alles klar.

    Bei Collections kann ich mir sowas auch gut vorstellen, immerhin ist das
    ja ne Framework-interne Angelegenheit, aber was is bei eigens definierten
    typisierten Klassen ? Werden da Wildcards überhaupt erlaubt ? Falls ja, wie
    unterscheidet der Compiler dann zwischen lesendem und schriebendem Zugriff ?

    Bin Java Anfänger und hab ne Zeit lang keinen Compiler zur Verfügung, die
    Frage interessiert mich aber jetzt schon, darum sorry für ne relativ dumme
    Frage ..

    grüße
    Niob



  • Es gibt keinen Unterschied zwischen Framework und deinen eigenen Klassen, das funktioniert also genau gleich.

    -> Wildcards sind erlaubt
    Wenn ein Wildcard da ist, kannst du nur lesen, es gibt nix anderes. Also eine relativ klare Sache (ich wüsst nicht, was der Compiler da gross zu überlegen hätte).



  • Danke für die schnelle Antwort.

    JBeni schrieb:

    Es gibt keinen Unterschied zwischen Framework und deinen eigenen Klassen, das funktioniert also genau gleich.

    Naja, ich dachte halt mal, dass der Compiler weiß welche
    Methoden vom Framework "konstant" sind und welche nicht ..

    // Is jetzt zwar keine sinnvolle Klasse, mir is halt
    // grad nichts anderes eingefallen ..
    public class MyCollection<T> {
        private ArrayList list;
    
        // ...
    
        public boolean add(T item) 
        {
            return list.add(item);
        }
    }
    
    class Tester {
        public static void addSomething(MyCollection<? extends Number> numbers)
        {
            numbers.add(5.0);
        }
    
        public static void doSomething(MyCollection<Integer> numbers)
        {
            addSomething(numbers);
        }
    }
    

    Wie überprüft der Compiler hier ob die Methode
    MyCollection.add erlaubt ist oder nicht ?

    grüße
    Niob



  • Indem er sich "<? extends Number>" anguckt, darin ein "?" findet, und daraus schliesst, "kein Schreibrecht" (der Compiler würde bei diesem Code also ein Fehler ausgeben).



  • JBeni schrieb:

    Indem er sich "<? extends Number>" anguckt, darin ein "?" findet, und daraus schliesst, "kein Schreibrecht" (der Compiler würde bei diesem Code also ein Fehler ausgeben).

    Was ist also dann erlaubt, nur iterieren ?

    grüße
    Niob



  • Wenn du ein

    public class MyCollection<E>{
      public void blupp( E e ){...}
    }
    

    hast: und ein "MyCollection<? extends Irgendwas>" nimmst, dann weisst du nicht, was "E" nun ist (deshalb das ?).
    Du kannst also jede Methode welche ein E als Argument enthält nicht aufrufen, weil nicht bekannt ist, was E ist.
    Du kannst aber jede Methode aufrufen die ein E zurückgibt, denn du weisst aus "? extends Irgendwas", dass ein E wenigstens ein "Irgendwas" sein muss.
    (Das hat mit Typsicherheit zu tun, wenn das E z.B. AdvancedIrgendwas wäre, und du schreibst ein normales Irgendwas, gibt es später ein Problem)



  • Schreibender Zugriff ist nicht grundsätzlich verboten. Es gilt:
    Jedesmal wenn ich T alleine schreibe, kann auch ein abgeleiteter Typ verwendet werden.

    Bivarianz <?>: Keine Zuweisung <?> -> T oder umgekehrt ist erlaubt.

    Kovarianz <? extends T>: Die Zuweisung T -> <? extends T> ist nicht erlaubt, wohl aber <? extends T> -> T
    ist auch sofort einsichtig: <? extends T> muss zuweisungskompatibel zu T sein, T oder etwas abgeleitetes aber nicht zwangsläufig zu ? extends T.
    Sei T == Number, dann könnte der Elementtyp bei <? extends T> Integer sein, dass zuzuweisende Objekt T (oder von T abgeleitet) aber ein Double. Double -> Integer geht nicht.

    Kontravarianz <? super T>: Die Zuweisung T (oder abgeleitet) -> <? super T> ist erlaubt, denn etwas, was von T abgeleitet ist, ist immer zuweisungskompatibel zu T oder einer Basisklasse von T!
    Beispiel:

    List<? super Integer> list =
    // eins von den dreien:
    // new List<Integer>();
    // new List<Number>();
    // new List<Object>();
    
    list.add(new Integer());
    list.add(... /* von Integer abgeleitet - gibt's in diesem Fall nichts, wäre aber erlaubt */ );
    

    Umgekehrt ist die Zuweisung <? super T> -> T (oder abgeleitet) natürlich nicht erlaubt, da eine Basisklasse von T beispielsweise Object sein könnte und das ist nicht zuweisungskompatibel zu T für T != Object.



  • Danke euch beiden, so macht das ganze dann Sinn. 🙂

    grüße
    Niob


Anmelden zum Antworten