为什么在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));
}
VS
public <T> T getFavorite(Class<T> type)
{
return (T) (favorites.get(type));
}
我只是没有区别,为什么编译器抱怨未经检查的警告。最后,两段代码都进行了显式转换(T) object
,不是吗?
答案 0 :(得分:7)
如果没有@SuppressWarnings("unchecked")
注释,java.lang.Class
的源代码会在编译期间产生警告 。不会生成警告,因为JDK类位于已编译的库中。我确信自己编译JDK会产生一些警告。
@Bohemian评论说这是一个“官方kludge”本质上是正确的,并且有许多这样的代码示例。 (另一个例子是java.lang.Enum#getDeclaringClass
。)使用是安全的,因为编写的逻辑是正确的,它存在,所以你不必自己编写这种丑陋的东西。
我的建议是不要过多考虑这个实现:重要的是java.lang.Class#cast
符合已检查强制转换的语义。
答案 1 :(得分: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;
}
这个安全网就是为什么当演员阵容涉及类型参数时,反射演员比普通演员更受欢迎。