【问题标题】:Why is casting the class of a generic to Class<T> unsafe?为什么将泛型的类强制转换为 Class<T> 不安全?
【发布时间】:2013-08-12 23:30:41
【问题描述】:

我正在创建一个MethodPointer 类,以便从 C++ 模拟函数指针的功能。起初,我只用 Objects 做所有事情,但后来我有了一个想法——为什么不让它真正通用呢?

问题出在这个构造函数上,它试图调用另一个签名为MethodPointer(Class&lt;T&gt; clazz, String methodName, Class&lt;?&gt; ... paramClasses)的构造函数:

public MethodPointer(T object, String methodName, Class<?> ... paramClasses) {
    this(object.getClass(), methodName, paramClasses);
    this.object = object;
}

我认为这会正常工作,但我收到以下编译器错误:

The constructor MethodPointer<T>(Class<capture#1-of ? extends Object>,
String, Class<?>[]) is undefined

所以,很困惑,我这样做了:

public MethodPointer(T object, String methodName, Class<?> ... paramClasses) {
    this((Class<T>) object.getClass(), methodName, paramClasses);
    this.object = object;
}

现在可以编译,但我收到以下警告:

Unchecked cast from Class<capture#1-of ? extends Object> to Class<T>

我想问题是我不明白Class&lt;capture#1-of ? extends Object&gt; 是什么意思。我认为由于T 的类型是从T object 参数推断出来的,因此调用object.getClass() 会返回Class 类型为Class&lt;T&gt; 的对象。不过,显然情况并非如此。有人能解开我的困惑吗?


完整的类声明和所有构造函数:

public class MethodPointer<T> {

    //Logger instance
    private static final Logger LOGGER = Logger.getLogger(MethodPointer.class);

    //Object fields
    private final Method method;
    private ArrayList<Object> args = new ArrayList<Object>();
    private T object = null;


    //Constructors
    public MethodPointer(Method method) {
        this.method = method;
    }

    public MethodPointer(Class<T> clazz, String methodName, Class<?> ... paramClasses) {
        Method theMethod = null;
        try {
            theMethod = clazz.getMethod(methodName, paramClasses);
        }
        catch(NoSuchMethodException nsme) {
            LogUtil.log(LOGGER, Level.ERROR, "Unable to find method " + methodName + " in " + clazz.getSimpleName(), nsme);
        }
        method = theMethod;
    }

    public MethodPointer(T object, String methodName, Class<?> ... paramClasses) {
        this((Class<T>) object.getClass(), methodName, paramClasses);
        this.object = object;
    }

【问题讨论】:

  • 顺便说一句,如果您愿意使用Guava,请查看他们的Invokable 课程。

标签: java generics casting type-inference


【解决方案1】:

SLaks 的回答指出 object.getClass() 衰减为 Class&lt;? extends Object&gt;,以解释编译错误。但是投到 Class&lt;T&gt;不安全

getClass“返回运行时类”它被调用的对象。例如,如果我们在 MethodPointer&lt;Number&gt; 内部,那么 object is-a Number 但它的运行时类型可能是 IntegerDouble 等。这告诉我们将object.getClass() 转换为Class&lt;T&gt; 是不安全的,因为Class&lt;Integer&gt;Class&lt;Number&gt; 是不同的对象,代表不同的类——这一区别似乎与您尝试做的事情的正确性非常相关。

那么解决办法是什么?好吧,不要投。坚持要从来电者那里拿Class&lt;T&gt;

【讨论】:

  • +1 如果T 是非最终类型,OP 可能会根据调用的构造函数得到不一致的结果。
  • 到处使用Class&lt;? extends T&gt;会有帮助吗? (除了在指向阴影 final 方法时给出奇怪的结果)
  • @SLaks 这肯定会有所帮助 - 改为使用它会是安全的。如果您查看我的编辑历史记录,我实际上忘记了getClass 不会返回Class&lt;? extends T&gt; :) 由于OP 使用getMethod,它也搜索超类,因此使用Class&lt;? extends T&gt; 似乎很好-但如果他想使用@ 987654343@(查找非公开方法),他需要准确的Class&lt;T&gt;
  • @SLaks:即使Class&lt;? extends T&gt; 在技术上也不安全。假设TList&lt;String&gt;,那么你就会有Class&lt;? extends List&lt;String&gt;&gt;,这是不安全的;只有Class&lt;? extends List&gt; 是安全的。使用Class&lt;? extends List&lt;String&gt;&gt;,您可以使用.cast() 在没有警告的情况下将某些内容投射到List&lt;String&gt;;但未检查类型参数。
  • 感谢您解释警告。这有助于解释为什么它不安全(这是最初的问题)。
【解决方案2】:

问题是getClass() 忽略了类型参数。

Javadocs 说:

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

例如,new ArrayList&lt;String&gt;().getClass() 返回 Class&lt;? extends ArrayList&gt;,而不是 Class&lt;? extends ArrayList&lt;String&gt;&gt;

这也意味着在类型参数上调用的getClass() 衰减为Class&lt;? extends Object&gt;

【讨论】:

  • 由于在MethodPointer构造函数的第一个参数中指定了类型参数T,向下转换是否安全?
  • @KevinBowersox:是的,它是安全的,但是getClass() 的编译时类型专门忽略了类型参数。
  • 不安全并不总是意味着不安全。它真正的意思是编译器(受 JLS 约束)不能确认类型安全。
  • @JeffGohlke 以下内容可能有助于了解capture#1-of 的含义:docs.oracle.com/javase/tutorial/java/generics/capture.html
  • @yshavit 我和你在一起 - 我提到你只是为了引起你的注意,因为你的(赞成的)评论似乎支持这是一个安全的演员表。
【解决方案3】:

如果你不需要指定的 Class-type,你可以使用

Class<?>

为你的参数。

所以你的类有一个未绑定的类型。

如果你想获得一个实例,你可以这样使用它:

obj.getClass().getDeclaredConstructor(Class<?> parameterTypes)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多