4

为什么cast在类上使用该方法Class<?>会在编译时产生未经检查的警告?

如果您查看 cast 方法,您会发现以下代码:

public T cast(Object obj) 
{
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj; // you can see there is a generic cast performed here
}

如果我进行泛型转换,编译器会抱怨说有unchecked warning.


其他背景信息

您可以在 Book Effective Java 2 版第 166 页(pdf 的)中找到我如何解决这个问题的示例。

作者写了这段代码

public <T> T getFavorite(Class<T> type) 
{
    return type.cast(favorites.get(type));
}

对比

public <T> T getFavorite(Class<T> type) 
{
    return (T) (favorites.get(type));
}

我只是不明白为什么编译器会抱怨未经检查的警告。最后,两段代码都进行了显式强制转换(T) object,不是吗?

4

2 回答 2

7

如果没有@SuppressWarnings("unchecked")注解,源代码会在编译期间java.lang.Class产生警告。不会产生警告,因为 JDK 类位于已编译的库中。我确信自己编译 JDK 会产生一些警告。

@Bohemian 的评论说这是一个“官方的组合”基本上是正确的,并且有很多这样的代码示例。(另一个例子是java.lang.Enum#getDeclaringClass。)使用它是安全的,因为它所写的逻辑是正确的并且它存在所以你不必自己编写这种丑陋的东西。

我的建议是不要过多考虑这个实现:重要的是java.lang.Class#cast符合检查强制转换的语义。

于 2014-05-26T05:23:05.197 回答
1

为了强制执行内存安全,Java 确保引用类型的变量实际上包含对该类型(或其子类型)对象的引用。强制转换指令可能会违反这个不变量,即我们可以这样写:

Object o = new Integer(42);
String s = (String) o;  // compiles, but throws ClassCastException at runtime

为了防止这种情况,强制转换指令将检查所引用对象的类型,如果不是,则抛出 ClassCastException。

在将泛型引入 Java 语言之前,以上内容适用于所有类型转换。但是,使用类型擦除实现的泛型,运行时不知道类型参数代表哪个类,因此如果涉及类型参数,则无法执行此检查。

这就是为什么规范区分检查强制转换(只有在类型正确时才会在运行时成功)和未检查强制转换(即使类型不正确也可能成功,导致堆污染,并且可能在晚点)。例如:

class C<T> {
    final T field;

    C(Object o) {
        this.field = (T) o; // unchecked. Will never throw a ClassCastException.
    }
}

boolean test() {
    C<String> c = new C<String>(42);
    return c.field.startsWith("hello"); // throws ClassCastException, even though there is no cast in the source code at this line!
}

也就是说,未经检查的强制转换可能是不安全的,应该避免。

在这样的背景下,很容易看出检查了反射强制转换(Class.cast() 方法),因为它们实际上在方法本身中实现了检查:

public T cast(Object obj) 
{
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}

而这个安全网就是为什么当投射涉及类型参数时,反射投射比普通投射更受欢迎。

于 2014-05-26T05:13:53.210 回答