2013-03-11 17 views
8
class HasId<I> {} 
class HasStringId extends HasId<String> {} 
class Alert<T extends /*Some*/Object> extends HasStringId {} 
class BaseController<M extends HasId<String>> { 
    // abstract Class<M> getModelClass(); 
} 
class AlertController extends BaseController<Alert> { // error here 
    // @Override Class<Alert> getModelClass() { 
    //  return Alert.class; 
    // } 
} 

compila bene su openjdk6, ma in OpenJDK7 dà:Perché questi farmaci generici non si compilano in OpenJDK7, ma fare in openjdk6

AlertController.java:50: error: type argument Alert is not within bounds of 
    type-variable T 
class AlertController extends BaseController<Alert> { 
             ^
    where T is a type-variable: 
    T extends HasId<String> declared in class BaseController 

Nota che non c'è rawtype avvertimento linea 50, a causa di allarme deve essere parametrizzato. Se lo faccio, ad es. extends BaseController<Alert<Object>>, codice compila. Ma non posso farlo, perché ho bisogno di implementare getModelClass().

AGGIORNAMENTO: si trattava di un bug nelle implementazioni di Java 6, risolto in Java 7: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182. (Ed ecco la mia domanda agli sviluppatori di compilatori: http://openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1-7-0-15-but-do-in-1-6-0-27-td121820.html)

+0

Cosa succede se si parametrizza come 'extends BaseController >'? (Inoltre, il messaggio di errore si lamenta di "Controller extends ...", ma il codice postato è "AlertController extends ...". Sei sicuro di avere la linea corretta?) –

+0

Perché c'è un downvote? –

+0

@TedHopp Se si parametrizza con 'Alert ', non è possibile implementare 'getModelClass()'. Non è stato possibile, fino a quando Paul Bellora ha suggerito un bel trucco per lanciare la Classe in Classe >. @PaulBellora non sono io. Grazie per il trucco! –

risposta

1

Ci sono casi documentati di compilatori Java 7 più severi rispetto ai compilatori Java 6 per varie sfumature di generici. Ci sono a number of casi documentati di compilatori Java 7. Questi casi sono spesso correlati alle specifiche linguistiche reali essendo diventati più specifici. L'errore probabilmente ha a che fare con l'uso di qualsiasi tipo grezzo essenzialmente "optando" per i generici sui tipi ereditati - anche se è corretto è discutibile.

MODIFICA: Non ho trovato questo problema nello list of JDK 7 incompatibilities. L'errore è riproducibile using sun-jdk-1.7.0_10, ma non con il compilatore Eclipse (che storicamente ha un track record molto migliore di javac quando si tratta di sfumature generiche). È necessario submit an Oracle bug.

Ecco una possibile soluzione:

class AlertController extends BaseController<Alert<?>> { 
    @Override 
    @SuppressWarnings("unchecked") 
    Class<Alert<?>> getModelClass() { 
     return (Class<Alert<?>>)(Class<?>)Alert.class; 
    } 
} 
+1

Ho trovato la segnalazione di bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182. Grazie per il trucco! –

2

La questione è se HasId<String> è un supertipo del tipo grezzo Alert. Le specifiche non sono molto chiare su questo problema.

Nello spirito di [4.8], anche i supertipi di un tipo non elaborato devono essere tutti cancellati. Quindi Alert dovrebbe avere un supertipo HasId, ma non HasId<String>. Tuttavia la sezione parla solo in termini di "super classi/interfacce", non in termini di "supertipi".

Nello spirito di [4.10], i supertipi vengono scoperti tramite supertipi diretti. Non è chiaro in che modo la sezione si applica ai tipi non elaborati. Probabilmente intende stabilire che raw Alert ha un supertipo diretto HasStringId. Sembra giusto. Quindi perché HasId<String> è un supertipo diretto di HasStringId, per transitività, HasId<String> è un supertipo di Alert!

La confusione è radicata nel fatto che ci sono in realtà due tipi HasStringId, uno normale, uno grezzo. Anche se HasStringId non è generico in sé, ha un supertipo generico, quindi ha senso parlare della versione originale di HasStringId.

La specifica non fa una distinzione tra normale e grezzo HasStringId. Questa è una svista.

Supponiamo di denotare il grezzo HasStringId come HasStringId', quindi [4.10] ha più senso ora. La super interfaccia diretta di Alert non elaborata è la HasStringId'. La super interfaccia diretta di HasStringId' non elaborata è raw HasId. Pertanto, HasId è un supertipo di Alert, non HasId<String>.

Vedere section 4 of JLS. Qui collego il JLS precedente, poiché JLS 7 presenta seri errori di modifica nella sezione 4.10.2

+0

Hai ragione, almeno i ragazzi di [email protected] dicono lo stesso: http://openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1 -7-0-15-ma-do-in-1-6-0-27-td121820.html –

1

Credo che questo abbia a che fare con il modo in cui la cancellazione viene gestita in assenza di parametri di tipo reali. Quando si fa riferimento a un tipo parametrizzato senza parametri di tipo, tutti i riferimenti a quei parametri vengono cancellati.

In questo caso, si utilizza un tipo parametrizzato Alert senza alcun parametro di tipo. Questo cancella tutti i parametri di tipo su Alert e le sue superclassi. Ciò causa la cancellazione del parametro di tipo HasId nella clausola extends di HasStringId. Alert quindi non sottoclasse HasId<String> perché HasStringId non lo estende più ma estende invece HasId.

La soluzione alternativa di Paul B. o quella di seguito evita questo problema utilizzando sempre Alert con i suoi parametri di tipo.

class AlertController<T> extends BaseController<Alert<T>> { 
    @Override Class<Alert<T>> getModelClass() { 
     return cast(Alert.class); 
    } 

    @SuppressWarnings("unchecked") 
    private <T> T cast(final Object o) { 
     return (T) o; 
    } 
}