2016-04-26 15 views
8

Sembra che sia di nuovo bloccato con java generics. Ecco quello che ho:Classi incompatibili di generici java

paio di classi:

class CoolIndex implements EntityIndex<CoolEntity>

class CoolEntity extends BaseEntity

Enum utilizzando le classi di cui sopra:

enum Entities { 
    COOL_ENTITY { 
     @Override 
     public <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls() { 
      return CoolIndex.class; 
     } 

     @Override 
     public <E extends BaseEntity> Class<E> getEntityCls() { 
      return CoolEntity.class; 
     } 
    } 

    public abstract <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls(); 

    public abstract <E extends BaseEntity> Class<E> getEntityCls();  
} 

Funzione ho bisogno di chiamare con l'utilizzo dei risultati delle getIndexCls() chiamata di funzione:

static <E extends BaseEntity, I extends EntityIndex<E>> boolean isSomeIndexViewable(Class<I> cls) 

Il problema è che il compilatore si lamenta return CoolIndex.class; e return CoolEntity.class; e non è chiaro per me perché ... Certo che posso gettarlo ai Class<I> (primo caso), ma sembra a me come sto cercando di mascherare la mia incomprensione e non sembra giusto.

+0

Correlati (può aiutare): http://stackoverflow.com/q/20973221/1225328 – sp00m

+0

Un paio di altre strategie: http://stackoverflow.com/questions/11490485/how-to-implement-enum-with-generics – azurefrog

+0

Un problema molto simile a http://stackoverflow.com/questions/28234960/java-generics -and-enum-loss-of-template-parameters – EpicPandaForce

risposta

4

Il problema con getIndexCls è che poiché è generico, i parametri di tipo possono essere interpretati come qualsiasi classe che si adatti ai limiti delle dichiarazioni. Si potrebbe pensare che CoolIndex.class si adatta questi limiti, e lo fa, ma il chiamante del metodo puoi fornire i propri argomenti di tipo, che sarebbero incompatibili, ad esempio:

Entities.COOL_ENTITY.<UncoolEntity, UncoolIndex>getIndexCls(); 

Che si romperebbe la sicurezza di tipo, in modo che il compilatore non consente questo . Puoi trasmettere a Class<I>, ma il compilatore ti avviserà di un cast non controllato per lo stesso motivo. Si compilerà, ma può causare problemi di runtime come ho descritto.

Altre situazioni possono risolvere una situazione del genere passando un oggetto Class<I> per rendere l'inferenza del tipo funzionante correttamente, ma che sconfigge il punto di questo metodo - restituendo un oggetto Class<I>.

Altre situazioni richiedono il trasferimento dei parametri di tipo generico dal metodo alla classe, ma si utilizzano le enumerazioni, che non possono essere generiche.

L'unico modo che ho trovato per ottenere qualcosa di simile da compilare è la rimozione del enum interamente. Utilizzare una classe astratta in modo da poter dichiarare i parametri di tipo a livello di classe. Istanzia le costanti con gli argomenti tipo che desideri.

abstract class Entities<E extends BaseEntity, I extends EntityIndex<E>> { 
    public static final Entities<CoolEntity, CoolIndex> COOL_ENTITY = new Entities<CoolEntity, CoolIndex>() { 
     @Override 
     public Class<CoolIndex> getIndexCls() { 
      return CoolIndex.class; 
     } 

     @Override 
     public Class<CoolEntity> getEntityCls() { 
      return CoolEntity.class; 
     } 
    }; 

    // Don't instantiate outside this class! 
    private Entities() {} 

    public abstract Class<I> getIndexCls(); 
    public abstract Class<E> getEntityCls(); 
} 
+0

Grazie per questa soluzione. Comunque, sembra un po 'sporco, ma è la soluzione più "giusta" in questo caso, immagino. –

1

Questo può essere riprodotto da molto più semplice esempio:

public <E extends BaseEntity> E get() { 
    return new BaseEntity(); // compilation error here 
} 

Il problema in tale dichiarazione <E extends BaseEntity> è che il metodo pretende di restituire un'istanza di qualsiasi tipo E che chiamante deve chiedere:

MyCoolEntity1 e = get(); // valid, E is MyCoolEntity1 
MyCoolEntity2 e = get(); // valid, E is MyCoolEntity2 

Questo codice deve essere sicuro in fase di compilazione, quindi è necessario trasmettere il risultato del metodo su E

public <E extends BaseEntity> E get() { 
    return (E) new BaseEntity(); // no error, but unsafe warning 
} 

Nel tuo esempio è abbastanza lo stesso, si pretende di valore di tipo Class<E> tornare:

public <E extends BaseEntity> Class<E> getEntityCls() 

Ma restituire una classe concreta SomeEntity.class che è Class<CoolEntity>


OK, come devo risolvere quella?

  1. È possibile aggiungere cast di tipo return (Class<I>) CoolIndex.class;/return (Class<E>) CoolEntity.class;

  2. È possibile sostituire enum con le classi, dal momento che le enumerazioni non possono essere generici e le classi possono

  3. È possibile eliminare del tutto generici, dato che non c'è non ha molto valore