如何在运行时获取通配符上限的类型?

How can I get at runtime the type of a wildcard upper bound?

假设我正在保留某个 class、T:

的子classes 的注册表
public class ClassRegistry<T> {
    Set<Class<? extends T>> klasses;
    ...
    public void register(Class<? extends T> klass) {
        klasses.add(klass);
    }

您通过 registry.register(this.getClass()) 这样的电话注册自己。我想通过 ClassRegistry<T> 上的方法简化此操作,您只需传递自己 this,例如 registry.register(this):

    public void register(Object obj) {
        Class<?> klass = obj.getClass();
        this.register(klass);
    }

糟糕,这是错误的,因为它会调用自身(将重载与参数类型 Object 匹配,当然不是 Class<? extends T>)。所以,改为:

    public void register(Object obj) {
        Class<? extends T> klass = obj.getClass();
        this.register(klass);
    }

现在当然无法编译,因为编译器不知道您的 obj 实际上是某种类型 ? extends T。它可能不会,因为调用者可能是错误的。

所以我的问题是:我如何 test/validate objT 的子 class,它是通配符上限(这样我就可以安全地投)? (我怀疑 - 或者也许希望 - 答案涉及 Guava 的 TypeToken,这就是我添加 Guava 标签的原因,但我会接受任何答案,谢谢!)

您不应该能够注册任何类型的任何对象,这就是类型 Object 在您的 register 方法中的含义。

我会将该参数的类型更改为 T。那么,你可以保证obj.getClass()会return一个Class<? extends T>。编译器不会完全同意这一点,因为 Javadocs for getClass() 状态:

The actual result type is Class where |X| is the erasure of the static type of the expression on which getClass is called.

调用getClass()的结果不是Class<? extends T>,而是Class<? extends Object>T的擦除)。您可以将其转换为 Class<? extends T>,这将生成一个未经检查的转换警告。但是,因为你现在 obj 是一个 T,我认为类型安全是保留的。

@SuppressWarnings("unchecked")
public void register(T obj) {
    Class<? extends T> klass = (Class<? extends T>) obj.getClass();
    this.register(klass);
}