2016-04-26 12 views
8

Wygląda na to, że ponownie utknąłem w generycznych elementach java. Oto co mam:Niezgodne klasy generyczne języka Java

Kilka klas:

class CoolIndex implements EntityIndex<CoolEntity>

class CoolEntity extends BaseEntity

Enum pomocą klas powyżej:

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();  
} 

Funkcja Muszę zadzwonić z wykorzystaniem wyniku getIndexCls() wywołanie funkcji:

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

Problemem jest to, że kompilator narzeka return CoolIndex.class; i return CoolEntity.class; i to nie jest dla mnie jasne, dlaczego ... Oczywiście mogę rzucić go do Class<I> (pierwszy przypadek), ale wydaje mi się, jakbym próbował zamaskować mój nieporozumienie i nie czuje się dobrze.

+0

Powiązane (może pomóc): http://stackoverflow.com/q/20973221/1225328 – sp00m

+0

Kilka innych strategii: http://stackoverflow.com/questions/11490485/how-to-implement-enum- with-generics – azurefrog

+0

Bardzo podobny problem do http://stackoverflow.com/questions/28234960/java-generics i-enum-loss-of-template-parameters – EpicPandaForce

Odpowiedz

4

Problem z getIndexCls polega na tym, że ponieważ jest on ogólny, parametry typu mogą być interpretowane jako dowolne klasy, które pasują do granic deklaracji. Możesz myśleć, że CoolIndex.class pasuje do tych granic, i to robi, ale rozmówca metody może dostarczyć swoje argumenty typu, które byłoby sprzeczne, np:

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

To by przerwać typu bezpieczeństwa, więc kompilator nie zezwala na to . Możesz przesłać do Class<I>, ale kompilator ostrzeże Cię o niezaznaczonej obsadzie z tego samego powodu. Kompiluje się, ale może powodować problemy w czasie wykonywania, tak jak to opisałem.

Inne sytuacje mogą rozwiązać taką sytuację, przekazując obiekt Class<I>, aby wnioskowanie typu działało poprawnie, ale to pokonuje punkt tej metody - zwracając obiekt Class<I>.

Inne sytuacje wymagają przeniesienia ogólnych parametrów typu z metody do klasy, ale używane są wyliczenia, które nie mogą być ogólne.

Jedyny sposób, w jaki wymyśliłem coś podobnego do kompilacji, to całkowite usunięcie enum. Użyj klasy abstrakcyjnej, aby móc zadeklarować parametry typu na poziomie klasy. Stwórz stałe z żądanymi argumentami typu.

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

Dzięki za to rozwiązanie. W każdym razie wygląda na trochę brudną, ale chyba jest to najbardziej "właściwe" rozwiązanie w tym przypadku. –

1

ten może być powielany przez wiele prostszy przykład:

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

Problemem w takiej deklaracji <E extends BaseEntity> jest to, że metoda twierdzi wrócić wystąpienie dowolnego typu E że rozmówca powinien zapytać:

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

Ten kod powinien być w trakcie kompilacji, więc musisz rzucić wynik swojej metody na E

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

W przykładzie jest to dość taka sama, twierdzisz, aby powrócić wartość typu Class<E>:

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

Ale powrót konkretną klasę SomeEntity.class który Class<CoolEntity>


OK, w jaki sposób należy ustalić że?

  1. Możesz dodać typ obsady return (Class<I>) CoolIndex.class;/return (Class<E>) CoolEntity.class;

  2. Można zastąpić enum z klas, ponieważ teksty stałe nie może mieć charakter ogólny i klasy mogą

  3. można całkowicie usunąć generyczne, ponieważ nie nie ma w tym żadnej wartości