Решите проблему Java Generic Type с абстрактным методом класса

Я хочу достичь чего-то подобного в java, но получаю ошибку времени компиляции:

Метод onMessage (TextMessage, Class) в типе AbstractTestLoader не применим для аргументов (TextMessage, Class)

Я понимаю причину этой ошибки, но я также чувствую, что должен быть какой-то способ добиться этого с помощью кастинга или может быть каким-то другим способом.

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
    }
}

Ответ 1

Наконец, после пары попыток я просто получаю простое и эффективное решение, но все же я открыт для прослушивания других лучших ответов, если это возможно. Благодаря

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
    }
}

Ответ 2

Следует заметить, что в то время как Java generic удалялся во время выполнения, существует ограниченное отражение apis для извлечения их, если они присутствуют в файле класса.

Вот быстрое решение с этими предположениями:

  • AbstractTestLoader - это прямой суперкласс.
  • Подкласс не использует групповой символ типа при объявлении суперкласса, например. таких подклассов, как этот class GenericLoader<T extends SubclassAbstractEntity<T>> extends AbstractTestLoader<T> не существует.

Вот код:

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
    }
}

getEntityType можно переопределить в подклассах, где эти два допущения терпят неудачу.

Ответ 3

Java реализует generics с помощью стирания типов, что означает, что это просто концепция времени компиляции. Когда программа запущена, нет разницы между AbstractTestLoader<Foo> и a AbstractTestLoader<Bar>.

Существует несколько обходных решений, например, тот, который вы обнаружили, когда класс, который знает тип T во время компиляции, может быть создан для обеспечения фактического типа класса. Но нет способа использовать чистые дженерики, чтобы узнать, что такое общий тип во время выполнения.