我想我明白了未经检查的演员表的含义(从一个不同类型的演员转换到另一个),但是“检查”演员表是什么意思?如何检查演员表,以便避免在 Eclipse 中出现此警告?
3 回答
详细说明彼得写的内容:
从非泛型类型到泛型类型的转换可能在运行时工作得很好,因为泛型参数在编译期间被删除,所以我们留下了合法的转换。ClassCastException
但是,由于对类型参数的错误假设,代码稍后可能会因意外而失败。例如:
List l1 = new ArrayList();
l1.add(33);
List<String> l2 = (List<String>) l1;
String s = l2.get(0);
第 3 行未经检查的警告表明编译器无法保证类型安全,因为以后可能会发生意外的 ClassCastException。实际上,这发生在执行隐式转换的第 4 行。
未经检查的强制转换意味着您(隐式或显式)从泛型类型转换为非限定类型或相反。例如这条线
Set<String> set = new HashSet();
会产生这样的警告。
通常出现此类警告是有充分理由的,因此您应该尝试改进代码而不是抑制警告。引自 Effective Java,第 2 版:
尽可能消除所有未经检查的警告。如果您消除了所有警告,则可以确保您的代码是类型安全的,这是一件非常好的事情。这意味着您不会
ClassCastException
在运行时得到 a,它增加了您对程序按预期运行的信心。如果您不能消除警告,并且您可以证明引发警告的代码是类型安全的,那么(并且只有这样)使用
@SuppressWarnings("unchecked")
注释来抑制警告。如果您在没有首先证明代码是类型安全的情况下抑制警告,那么您只会给自己一种错误的安全感。代码可以在不发出任何警告的情况下编译,但它仍然可以ClassCastException
在运行时抛出 a。但是,如果您忽略了您知道是安全的未经检查的警告(而不是抑制它们),您将不会注意到何时出现代表真正问题的新警告。新的警告将在您没有消除的所有误报中消失。
当然,消除警告并不总是像上面的代码那样容易。如果没有看到您的代码,则无法说明如何使其安全。
与检查强制转换相反,未检查强制转换不会在运行时检查类型安全。
这是一个基于Consider typesafe heterogenous containers
第 3 版部分的示例。Joshua Bloch 的“Effective Java”,但容器类被故意破坏 - 它存储并返回错误的类型:
public class Test {
private static class BrokenGenericContainer{
private final Map<Class<?>, Object> map= new HashMap<>();
public <T> void store(Class<T> key, T value){
map.put(key, "broken!"); // should've been [value] here instead of "broken!"
}
public <T> T retrieve(Class<T> key){
// return key.cast(map.get(key)); // a checked cast
return (T)map.get(key); // an unchecked cast
}
}
public static void main(String[] args) {
BrokenGenericContainer c= new BrokenGenericContainer();
c.store(Integer.class, 42);
List<Integer> ints = new ArrayList<>();
ints.add(c.retrieve(Integer.class));
Integer i = ints.get(0);
}
}
如果retrieve()
使用未经检查的演员表- (T)map.get(key)
- 运行此程序将导致ClassCastException
发生Integer i = ints.get(0)
在线。该retrieve()
方法将完成,因为在运行时未检查实际类型:
Exception in thread "main"
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at Test.main(Test.java:27)
但是如果retrieve()
使用checked cast——运行
key.cast(map.get(key))
这个程序会导致ClassCastException
发生在key.cast(map.get(key))
line,因为checked cast会发现类型错误并抛出异常。该retrieve()
方法将无法完成:
Exception in thread "main" java.lang.ClassCastException:
Cannot cast java.lang.String to java.lang.Integer
at java.lang.Class.cast(Class.java:3369)
at Test$BrokenGenericContainer.retrieve(Test.java:16)
at Test.main(Test.java:26)
看起来差别不大,但在未经检查的演员表的情况下, aString
成功进入了List<Integer>
. 在现实世界的应用中,这种情况的后果可能是……嗯,很严重。在检查强制转换的情况下,尽早发现类型不匹配。
@SuppressWarnings("unchecked")
如果程序员确实确定该方法实际上是安全的,则可以使用以避免未经检查的强制转换警告。更好的选择是尽可能使用泛型和检查强制转换。
正如约书亚·布洛赫所说,
...未经检查的警告很重要。不要忽视他们。
为了完整起见,此答案涉及 Eclipse 细节。