使用抽象 Class 方法解决 Java 通用类型错误

Solve Java Generic Type Error with Abstract Class Method

我想在 java 中实现类似的功能,但出现编译时错误:

The method onMessage(TextMessage, Class) in the type AbstractTestLoader is not applicable for the arguments (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
    }
}

最后,经过几次尝试,我得到了简单有效的解决方案,但如果可能的话,我仍然愿意听取其他最佳答案。谢谢

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

Java implements generics via type erasure,这意味着它只是一个编译时概念。当程序是 运行 时,AbstractTestLoader<Foo>AbstractTestLoader<Bar> 之间没有区别。

有一些变通方法,比如您发现的那个,其中可以使在编译时知道 T 类型的 class 提供实际的 class类型。但是无法使用纯泛型在运行时发现泛型类型。

应该注意,虽然 Java 泛型在运行时被删除,但如果它们存在于 class 文件中,则只有有限的反射 API 可以检索它们。

这里有一个基于这些假设的快速解决方案:

  1. AbstractTestLoader是直接超class.
  2. Sub classes 在声明 super class 时不使用类型通配符,例如class像这样的 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 可以在子class 中被覆盖,这两个假设都失败了。