2009-08-18 8 views
16

Java kann häufig auf Basis der Argumente (und sogar auf dem Rückgabetyp im Gegensatz zu beispielsweise C#) auf Generika schließen.Geerbte Platzhaltergenerika im Rückgabetyp

Typischer Fall: Ich habe eine generische Klasse Pair<T1, T2> bekam, die nur ein paar Werte speichert und kann auf folgende Weise verwendet werden:

Pair<String, String> pair = Pair.of("Hello", "World"); 

Die Methode of sieht genauso aus wie folgt aus:

public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
    return new Pair<T1, T2>(first, second); 
} 

Sehr schön. Allerdings ist dies nicht mehr funktioniert für die folgenden Anwendungsfall, die Platzhalter erfordert: (. Beachten Sie die explizite Umwandlung List.class den richtigen Typ zu machen)

Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello"); 

Der Code schlägt mit dem folgenden Fehler (vorausgesetzt von Eclipse):

Typenkonflikt: kann nicht von TestClass.Pair<Class<capture#1-of ?>,String> zu TestClass.Pair<Class<?>,String> konvertieren

Allerdings

, expliziten Aufruf der Konstruktor funktioniert immer noch wie erwartet:

Pair<Class<?>, String> pair = 
    new Pair<Class<?>, String>((Class<?>) List.class, "hello"); 

Kann jemand dieses Verhalten erklären? Ist es von Entwurf? Ist es gesucht? Mache ich etwas falsch oder bin ich auf einen Fehler im Design/Bug des Compilers gestoßen?

wilde Vermutung: die „capture # 1-of?“ Scheint irgendwie zu implizieren, dass der Platzhalter wird vom Compiler on the fly ausgefüllt, die Art machte ein Class<List> und damit sie nicht die Umwandlung (Pair<Class<?>, String>-Pair<Class<List>, String>) . Ist das richtig? Gibt es eine Möglichkeit, dies zu umgehen?


Der Vollständigkeit halber, hier ist eine vereinfachte Version der Pair Klasse:

public final class Pair<T1, T2> { 
    public final T1 first; 
    public final T2 second; 

    public Pair(T1 first, T2 second) { 
     this.first = first; 
     this.second = second; 
    } 

    public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
     return new Pair<T1, T2>(first, second); 
    } 
} 
+0

Es sieht so aus, als ob der Konverter die Signatur von "of" sieht, wenn er ein Paar zurückgibt ,? erweitert Klasse > Typ. Für die letzten Klassen scheint es schlau genug zu sein, den extend-Teil zu reduzieren, deshalb beschweren sie sich nicht über den String. – Zed

+0

Hmmm, interessant. Danke, dass du mich hier verlinkt hast. – jjnguy

+0

Es funktioniert jetzt in Java8. Der Zieltyp wird auch für die Inferenz herangezogen. – ZhongYu

Antwort

13

Der Grund der Konstruktor funktioniert, ist, dass Sie explizit die Typparameter sind angeben. Die statische Methode funktioniert auch, wenn Sie das tun:

Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello"); 

Natürlich ist der ganze Grund, warum Sie eine statische Methode in erster Linie haben, ist wahrscheinlich nur die Typinferenz zu bekommen (die mit Konstrukteuren funktioniert nicht an alle). Das Problem hier (wie Sie vorgeschlagen haben) ist, dass der Compiler capture conversion ausführt. Ich glaube, dies als Folge der [§15.12.2.6 of the JLS] ist:

  • Der Ergebnistyp der gewählten Methode wird wie folgt bestimmt:
    • Wenn die Methode aufgerufen wird, wird mit einem Rückgabetyp für nichtig erklärt, dann ist das Ergebnis ungültig.
    • Wenn die ungeprüfte Konvertierung erforderlich ist, damit die Methode anwendbar ist, ist der Ergebnistyp das Löschen (§4.6) von der deklarierte Rückgabetyp der Methode.
    • Andernfalls, wenn die Methode aufgerufen wird, ist generisch, dann für 1in, lassen Fi die formalen Parameter des Typs von die Methode sein, lassen Sie Ai der tatsächliche Typ sein Argumente für die Methode Aufruf geschlossen, und sei R die sein deklariert Rückgabetyp der Methode ist aufgerufen. Der Ergebnistyp wird erhalten, indem die Erfassungskonvertierung (§5.1.10) auf R [F1: = A1, ..., Fn: = An] angewendet wird.
    • Andernfalls wird der Ergebnistyp durch Anwenden der Erfassung Konvertierung (§5.1.10) auf den Typ in der Methodendeklaration erhalten.

Wenn Sie wirklich den Schluss wollen, ist eine mögliche Abhilfe ist, so etwas zu tun:

Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello"); 

Die Variable pair wird eine breitere Art haben, und es tut ein bisschen bedeuten Geben Sie den Typnamen der Variablen ein, aber Sie müssen den Methodenaufruf nicht mehr einbetten.

+0

Vielen Dank. Ich überlege noch, ob der Workaround den Kern nicht noch verwirrender macht. Für den Moment werde ich es so lassen wie es ist. –

Verwandte Themen