【发布时间】:2011-02-11 04:47:06
【问题描述】:
我想我明白了未经检查的演员表的含义(从一个不同类型的演员转换到另一个),但是“检查”演员表是什么意思?如何检查演员表以避免在 Eclipse 中出现此警告?
【问题讨论】:
-
请勿发布代码!不要让他们给你一条鱼而不是教你钓鱼:) 不,说真的,即使问题是以业余方式提出的,是否应该有一个通用问题的通用答案。
标签: java eclipse casting unchecked
我想我明白了未经检查的演员表的含义(从一个不同类型的演员转换到另一个),但是“检查”演员表是什么意思?如何检查演员表以避免在 Eclipse 中出现此警告?
【问题讨论】:
标签: java eclipse casting unchecked
详细说明彼得写的内容:
从非泛型类型到泛型类型的转换可能在运行时工作得很好,因为泛型参数在编译期间被删除,所以我们留下了合法的转换。但是,由于对类型参数的错误假设,代码稍后可能会因意外的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,它 增加您对程序按预期运行的信心。如果你不能消除警告,你可以证明代码 引发警告是类型安全的,然后(并且只有这样)抑制警告 带有
@SuppressWarnings("unchecked")注释。如果你禁止警告 没有首先证明代码是类型安全的,你只是给自己一个 虚假的安全感。代码可以在不发出任何警告的情况下编译,但是 它仍然可以在运行时抛出ClassCastException。但是,如果您忽略 你知道是安全的未经检查的警告(而不是压制它们),你 当出现代表真正问题的新警告时,不会注意到。这 新的警告会在你没有消除的所有误报中消失。
当然,消除警告并不总是像上面的代码那样容易。但是,如果没有看到您的代码,就无法知道如何使其安全。
【讨论】:
FoodBag<?> bag2 = new FoodBag<CheeseSandwich>();((FoodBag<CheeseSandwich>)bag2).setFood(new CheeseSandwich()); 从泛型类型到泛型类型?
与检查的强制转换相反,未检查的强制转换不会在运行时检查类型安全。
这是一个基于第三版Consider typesafe heterogenous containers 部分的示例。 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)) 行,因为检查的强制转换会发现类型错误并抛出异常。 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)
看起来差别不大,但在未检查演员表的情况下,String 成功进入List<Integer>。在现实世界的应用中,这种情况的后果可能是……嗯,很严重。如果使用检查型强制转换,则可以尽早发现类型不匹配。
为避免未经检查的强制转换警告,如果程序员确实确定该方法实际上是安全的,则可以使用@SuppressWarnings("unchecked")。更好的选择是尽可能使用泛型和检查型强制转换。
正如约书亚·布洛赫所说,
...未经检查的警告很重要。不要忽视它们。
为了完整起见,this 回答涉及 Eclipse 细节。
【讨论】: