2011-11-18 9 views
5

Ho scoperto i seguenti problemi con i generici. Si consideri la generica interfacciaAccesso agli argomenti dei tipi generici utilizzati nelle barre dei tipi

public interface A<X> { 
    X get(); 
    void doStuff(X x); 
} 

Ora, supponiamo che la seguente definizione del metodo:

public <T extends A<?>> void foo(T t) { 
    bar(t); 
} 

A causa del carattere jolly, le informazioni sul tipo per il tipo di ritorno di get() è insufficiente. Pertanto, ho delegare ad un altro metodo che "lega" questo jolly per un tipo di variabile fresco:

private <X> void bar(A<X> t) { 
    X x = t.get(); 
    t.doStuff(x); 
} 

L'invocazione del bar() in foo non è permesso, il compilatore emette il seguente messaggio di errore:

la barra metodo (a) nella prova di tipo non è applicabile per l'argomenti (T)

Tuttavia, se cambio il metodo foo() per

public <T extends A<?>> void foo(T t) { 
    A<?> u = t; // No explicit cast required, no "unchecked" warning! 
    bar(u); 
} 

funziona. Perché? È un errore del compilatore? Qualsiasi commento su questo sarebbe molto apprezzato.

Note:

  • Il motivo per cui io non semplicemente dichiaro metodo foo come void foo (A) è che sto in realtà utilizzando il tipo superiore interesection legato (&).
  • Il motivo per cui non dichiaro X come variabile di tipo in foo() è che effettivamente ho il problema a livello di classe e non voglio aumentare inutilmente il numero di parametri di tipo di questa classe.
+0

stai usando il flag del compilatore -Xlint? In caso contrario, prova a compilarlo con quello –

+0

Non cambia nulla .. – misberner

+0

Sono d'accordo con @kan. Il compilatore java, in effetti, lo compila. Ho usato 1.6.0_27. Eclipse ti dà l'errore esatto che hai dato, quindi forse lo stai usando? –

risposta

2

Ho controllato il codice:

public class Test 
{ 
    public interface A<X> { 
     X get(); 
     void doStuff(X x); 
    } 
    public <T extends A<?>> void foo(T t) { 
     bar(t); 
    } 

    private <X> void bar(A<X> t) { 
     X x = t.get(); 
     t.doStuff(x); 
    } 
} 

funziona. javac 1.6.0_22. Dove hai l'errore? O sto usando un altro codice?

+0

Grazie, lo controllerò lunedì sulla mia macchina al lavoro, non so esattamente quale versione di javac sto usando lì. L'ho appena provato a casa e non viene compilato usando javac 1.6.0_11 ... – misberner

+0

Btw, l'errore è semplicemente " bar (Test.A ) in Test non può essere applicato a (T)", con il marcatore puntando alla chiamata bar(). – misberner

1

Il codice:

public class Test 
{ 
    public interface A<X> { 
     X get(); 
     void doStuff(X x); 
    } 
    public <T extends A<?>> void foo(T t) { 
     bar(t); 
    } 

    private <X> void bar(A<X> t) { 
     X x = t.get(); 
     t.doStuff(x); 
    } 
} 

compila solo per le versioni selezionate di Java 1.6. Questo bug è stato registrato per Eclipse, ma è stata respinta con la seguente spiegazione da Srikanth Sankaran:

Credo che il comportamento eclisse compilatore è corretto e si sposa con JDK5 e JDK7 (l'ultima). Sembrerebbe che il comportamento di JDK6 sia una regressione che è stata riparata da allora.

In sostanza, ecco una breve spiegazione di ciò che sta succedendo qui:

Data la barra di metodo generico e il sito chiamata come data dal bar (t), l'algoritmo di inferenza non ha vincoli con cui lavorare per dedurre il tipo della variabile di tipo X. Quindi, dopo aver considerato gli argomenti e il tipo di ritorno atteso , X non è ancora risolto e per la specifica viene risolto il limite inferiore pubblicato - i.e X viene considerato come Object e il metodo diventa void bar (A) t); Poiché i parametri attuali non possono essere convertiti in parametri formali, il metodo dedotto deve essere rifiutato lasciandoci senza candidati applicabili. Quindi la chiamata deve essere rifiutata.

Motivo della barra della chiamata ((A) t); succede che in questo caso X viene interpretato come "cattura n. 1 di?" e il metodo generico parametrizzato con questa sostituzione diventa la barra vuota (A) per la quale è valida la compatibilità dei parametri.