Domanda Risolvi l'errore di tipo generico Java con il metodo astratto della classe


Voglio ottenere qualcosa di simile in java, ma ottenere un errore in fase di compilazione:

Il metodo onMessage (TextMessage, Class) nel tipo   AbstractTestLoader non è applicabile per gli argomenti   (TextMessage, Class)

Capisco la ragione di questo errore, ma sento anche che ci dovrebbe essere un modo per raggiungere questo obiettivo con il cast o potrebbe essere un altro modo.

public abstract class AbstractTestLoader<T extends AbstractEntity<T>> {

    public void onMessage(TextMessage message) throws Exception {
        onMessage(message, this.getClass()); // I want to correct this line in such a way so that I can call below method with actual Class of {T extends AbstractEntity<T>}
    }

    public void onMessage(TextMessage message, Class<T> clazz) throws Exception {
        //here my original logic will go
    }
}

11
2018-03-24 04:13


origine


risposte:


Dovrebbe essere notato che mentre Java è stato cancellato in runtime generico, ci sono degli apis di riflessione limitati per recuperarli se sono presenti nel file di classe.

Ecco una soluzione rapida con questi presupposti:

  1. AbstractTestLoader è la super classe diretta.
  2. Le sottoclassi non utilizzano il carattere jolly di tipo quando si dichiara una super classe, ad es. sottoclassi come questa class GenericLoader<T extends SubclassAbstractEntity<T>> extends AbstractTestLoader<T> non esiste.

Ecco il codice:

public class AbstractTestLoader<T extends AbstractEntity<T>> {

    private static final ClassValue<Class<?>> TYPE_PARAMETER_CACHE = new ClassValue<Class<?>>() {
        @Override
        protected Class<?> computeValue(Class<?> type) {
            assert AbstractTestLoader.class.isAssignableFrom(type);
            assert type.getSuperclass() == AbstractTestLoader.class;
            Type genericSuperclass = type.getGenericSuperclass();
            assert genericSuperclass instanceof  ParameterizedType;
            Type entityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
            assert entityType instanceof  Class<?>;
            return (Class<?>) entityType;
        }
    };

    @SuppressWarnings("unchecked")
    protected Class<T> getEntityType() {
        return (Class<T>) TYPE_PARAMETER_CACHE.get(this.getClass());
    }

    public void onMessage(Object message) throws Exception {
        onMessage(message, getEntityType()); // getting compile time error here
    }

    public void onMessage(Object message, Class<T> clazz) throws Exception {
        //here my original logic will go
    }
}

Il getEntityType può essere sovrascritto in sottoclassi in cui quelle due ipotesi falliscono.


5
2018-03-24 04:43



Alla fine, dopo un paio di tentativi, ho una soluzione semplice e funzionante, ma sono comunque aperto a sentire altre risposte migliori, se possibile. Grazie

public abstract class AbstractTestLoader<T extends AbstractEntity<T>> {

    abstract Class<T> getEntityType();

    public void onMessage(TextMessage message) throws Exception {
        onMessage(message, getEntityType());
    }

    public void onMessage(TextMessage message, Class<T> clazz) throws Exception {
        //here my original logic will go
    }
}

4
2018-03-24 04:33



Java implementa i generici via tipo cancellato, il che significa che è solo un concetto in fase di compilazione. Quando il programma è in esecuzione, non c'è differenza tra un AbstractTestLoader<Foo> e a AbstractTestLoader<Bar>.

Esistono alcune soluzioni alternative, come quella che hai scoperto, in cui una classe è a conoscenza del tipo di T al momento della compilazione può essere fatto per fornire un tipo di classe reale. Ma non c'è modo di usare generici puri per scoprire cosa è il tipo generico in fase di runtime.


2
2018-03-24 04:37