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

时间:2015-08-28 18:26:47

标签: java generics reflection guava type-erasure

假设我正在保留某个类的子类注册表T

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。它可能不会,因为调用者可能是错的。

所以我的问题是:我如何测试/验证objT的子类,它是一个通配符上限(所以我可以安全地转换)? (我怀疑 - 或许,希望 - 答案涉及番石榴TypeToken,这就是为什么我添加了番石榴标签,但我会接受任何答案,谢谢!)

1 个答案:

答案 0 :(得分:4)

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

我会将该参数的类型更改为T。然后,您可以保证obj.getClass()将返回Class<? extends T>。编译器不太同意,因为Javadocs for getClass()状态:

  

实际结果类型是Class,其中| X |是擦除调用getClass的表达式的静态类型。

getClass()的调用结果不是Class<? extends T>,而是Class<? extends Object>T的删除)。您可以将其投射到Class<? extends T>,这将生成未经检查的投射警告。但是,因为您现在objT,我认为类型安全性会得到保留。

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