Generics: Verständnisfrage zu Wildcards

Ich habe eine abstrakte Klasse Either<A,B> mit den Unterklassen Left<A,B> (ein Wrapper für ein A) und Right<A,B> (ein Wrapper für ein B), also im Prinzip eine typsichere Version einer Union aus C/C++. Nun wollte ich eine Methode schreiben, die aus einer Liste von Eithers nur die “linken” Werte extrahiert. Fehlversuch:

static <A> List<A> lefts(List<Either<A,?> list) {
   ...
}

... 

List<Either<String,Integer>> eithers = ...
List<String> strings = lefts(eithers);   //<--------- Bumm!!!

java: method lefts in class org.highj.data.collection.Either<A,B> cannot be applied to given types;
  required: org.highj.data.collection.List<org.highj.data.collection.Either<A,?>>
  found: org.highj.data.collection.List<org.highj.data.collection.Either<java.lang.String,java.lang.Integer>>
  reason: cannot infer type-variable(s) A
    (argument mismatch; org.highj.data.collection.List<org.highj.data.collection.Either<java.lang.String,java.lang.Integer>> cannot be converted to org.highj.data.collection.List<org.highj.data.collection.Either<A,?>>)

Die Lösung ist, die Methoden-Signatur auf static <A> List<A> lefts(List<? extends Either<A,?>> list) zu ändern. Nun dachte ich eigentlich, dass ich Wildcards einigermaßen begriffen habe, aber das verstehe ich beim besten Willen nicht. Wieso kann List<Either<A,?>> nicht mit einer List<Either<String,Integer>> aufgerufen werden, und warum klappt es dann mit dem zusätzlichen ? extends ...?

Eigentlich ist dieser Fall analog zur Frage, warum man eine Methode void foo(List<Number> numbers) nicht mit einer List<Integer> aufrufen kann: Nur weil die Typargumente (Number <- Integer) zuweisbar sind, gilt das noch nicht für die Instantiierungen der damit parametrisierten Typen. Either<String,?> ist ein supertyp von Either<String,Integer>. Man könnte ja in eine List<Either<String,?>> sowohl ein Either<String,Integer> reinlegen, als auch ein Either<String,String>. Irgendwo würde damit (vermutlich, mal wieder) die Typsicherheit kaputt gehen.

(Aber ich will nicht behaupten, dass ich nicht gelegentlich (oder öfter, als ich zuzugeben bereit bin) in dieses Muster verfalle: “WTF?! Naja, mal ein paar ? extends einstreuen, bis Eclipse sich nicht mehr beschwert” :o Spätestens bei Verschachtelungen (wie in diesem Fall) und Wildcards (wie in diesem Fall) wird es schon schnell schwer nachvollziehbar).

BTW: Ggf. könnte man das auch mit einem unbenutzten Wegwerf-Typargument lösen, wenn man das will:

static <A,X> List<A> lefts4(List<Either<A,X>> list) { ... }

Das wird dann beim Aufruf an was konkretes gebunden, aber an WAS, ist egal - das könnte etwa das sein, was du erreichen wolltest…

EDIT: Ja, wo die Typsicherheit weg wäre, ist sogar recht leicht zu erkennen:

    static <A> List<A> lefts(List<Either<A,?>> list) 
    {
        Either<A, String> foo = null; // Zweites Argument ist 'String'
        list.add(foo); // <- Wäre blöd, wenn 'list' hier eine List<Either<A, Integer>> wäre....
        return null;
    }

IMHO klappt das nicht, weil es zwei verschiedene Typen sind. Das ist das gleiche wie die Definition einer Methode die eine List als Parameter verwendet und man versucht eine List zu übergeben. Beides sind Typen von Listen aber List ist kein Subtyp des parametrisierten Typen List. Sagt man jedoch das eine Liste vom Typ:Typen abgeleitet von Object ist (List<? extends Object> funktioniert es wieder.