2009-08-18 10 views
16

Java può spesso dedurre generici basati sugli argomenti (e anche sul tipo restituito, al contrario di ad esempio C#).Generics con caratteri jolly dedotti nel tipo di ritorno

Caso in questione: io ho una classe generica Pair<T1, T2> che ha appena memorizza una coppia di valori e può essere utilizzato nel modo seguente:

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

Il metodo of sembra proprio come questo:

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

Molto bello. Tuttavia, questo non lavora più per il seguente caso d'uso, che richiede i caratteri jolly: (. Si noti il ​​cast esplicito di rendere List.class il tipo corretto)

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

Il codice non riesce con il seguente errore (a condizione da Eclipse):

Tipo non corrispondente: non può convertire da TestClass.Pair<Class<capture#1-of ?>,String> a TestClass.Pair<Class<?>,String>

Tuttavia, in modo esplicito chiamando il costruttore funziona ancora come previsto:

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

qualcuno può spiegare questo comportamento? È di progettazione? È desiderato? Sto facendo qualcosa di sbagliato o mi sono imbattuto in un difetto nella progettazione/bug nel compilatore?

indovinare: la “cattura # 1-di?” Sembra in qualche modo implicare che il carattere jolly viene compilato dal compilatore al volo, rendendo il tipo di un Class<List>, e non riuscendo in tal modo la conversione (Pair<Class<?>, String>-Pair<Class<List>, String>) . È giusto? C'è un modo per aggirare questo?


Per completezza, qui è una versione semplificata della classe Pair:

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

Sembra che il convertitore veda la firma di "of" mentre restituisce una coppia ,? estende Classe > tipo. Per le classi finali sembra abbastanza intelligente da ridurre la parte estesa, ecco perché non si lamenta della stringa. – Zed

+0

Hmmm, interessante. Grazie per avermi collegato qui. – jjnguy

+0

Ora funziona con java8. Anche il tipo di bersaglio viene consultato per inferene. – ZhongYu

risposta

13

Il motivo per il costruttore funziona è che si sta specificare esplicitamente i parametri di tipo. Il metodo statico anche funziona se si fa questo:

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

Naturalmente, tutta la ragione si ha un metodo statico in primo luogo è probabilmente solo per ottenere il tipo di inferenza (che non funziona con i costruttori a tutti).

Il problema qui (come suggerito) è che il compilatore sta eseguendo capture conversion. Credo che questo è il risultato di [§15.12.2.6 of the JLS]:

  • Il tipo risultato del metodo scelto è determinata come segue:
    • Se il metodo essendo richiamato viene dichiarata con un tipo di ritorno di vuoto, allora il risultato è nullo.
    • Altrimenti, se fosse necessaria la conversione deselezionata per applicare il metodo , il tipo di risultato è la cancellazione (§4.6) di del tipo di reso dichiarato del metodo.
    • Altrimenti, se il metodo chiamato è generico, poi per 1in, lascia Fi sia i parametri di tipo formali metodo, lasciate Ai sia il tipo effettivo argomentazioni dedotte per il metodo invocazione, e sia R la dichiarato tipo di ritorno del metodo invocato. Il tipo di risultato si ottiene applicando la conversione di cattura (§5.1.10) a R [F1: = A1, ..., Fn: = An].
    • In caso contrario, il tipo di risultato viene ottenuto applicando la cattura (§5.1.10) al tipo dato nella dichiarazione del metodo.

Se si vuole veramente l'inferenza, una possibile soluzione è quella di fare qualcosa di simile:

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

La variabile pair avrà un tipo più ampio, e vuol dire un po ' più digitando il nome del tipo di variabile, ma almeno non è più necessario eseguire il cast nella chiamata del metodo.

+0

Grazie mille. Sto ancora valutando se la soluzione alternativa non rende il nucleo ancora più confuso. Per il momento, lo lascerò così com'è. –