【问题标题】:What is unchecked cast and how do I check it?什么是未经检查的演员表,我该如何检查?
【发布时间】:2011-02-11 04:47:06
【问题描述】:

我想我明白了未经检查的演员表的含义(从一个不同类型的演员转换到另一个),但是“检查”演员表是什么意思?如何检查演员表以避免在 Eclipse 中出现此警告?

【问题讨论】:

  • 请勿发布代码!不要让他们给你一条鱼而不是教你钓鱼:) 不,说真的,即使问题是以业余方式提出的,是否应该有一个通用问题的通用答案。

标签: java eclipse casting unchecked


【解决方案1】:

详细说明彼得写的内容:

从非泛型类型到泛型类型的转换可能在运行时工作得很好,因为泛型参数在编译期间被删除,所以我们留下了合法的转换。但是,由于对类型参数的错误假设,代码稍后可能会因意外的ClassCastException 而失败。例如:

    List l1 = new ArrayList();
    l1.add(33);
    List<String> l2 = (List<String>) l1;
    String s = l2.get(0);

第 3 行未经检查的警告表明编译器无法保证类型安全,因为稍后可能会发生意外的 ClassCastException。实际上,这发生在执行隐式转换的第 4 行。

【讨论】:

  • 简单直接的解释+1
【解决方案2】:

未经检查的转换意味着您(隐式或显式)从泛型类型转换为非限定类型或相反。例如。这一行

Set<String> set = new HashSet();

会产生这样的警告。

此类警告通常是有充分理由的,因此您应该尝试改进您的代码,而不是隐藏警告。引自 Effective Java,第 2 版:

尽可能消除所有未经检查的警告。如果你 消除所有警告,确保您的代码是类型安全的,这是一个非常 好东西。这意味着你不会在运行时得到ClassCastException,它 增加您对程序按预期运行的信心。

如果你不能消除警告,你可以证明代码 引发警告是类型安全的,然后(并且只有这样)抑制警告 带有@SuppressWarnings("unchecked") 注释。如果你禁止警告 没有首先证明代码是类型安全的,你只是给自己一个 虚假的安全感。代码可以在不发出任何警告的情况下编译,但是 它仍然可以在运行时抛出ClassCastException。但是,如果您忽略 你知道是安全的未经检查的警告(而不是压制它们),你 当出现代表真正问题的新警告时,不会注意到。这 新的警告会在你没有消除的所有误报中消失。

当然,消除警告并不总是像上面的代码那样容易。但是,如果没有看到您的代码,就无法知道如何使其安全。

【讨论】:

  • 这个未经检查的强制转换怎么样:FoodBag&lt;?&gt; bag2 = new FoodBag&lt;CheeseSandwich&gt;();((FoodBag&lt;CheeseSandwich&gt;)bag2).setFood(new CheeseSandwich()); 从泛型类型到泛型类型?
  • @Datoraki,您的问题能否更具体一点?
  • -1 因为答案没有解决问题的“如何检查演员表”部分。好的,我们知道我们应该检查演员阵容,如何我们该怎么做?
  • @Mike,您还没有展示一个具体的代码示例,您对我们的具体解决方案有何期待?正如我在其他答案中所指出的那样,这是一个复杂的话题,没有万能的解决方案。
  • 这个问题没有得到解答。用注释隐藏它不是答案,有时无法更改代码以正确避免警告。警告本身似乎表明有某种方法可以检查演员表。但程序化测试似乎并没有做到这一点。
【解决方案3】:

与检查的强制转换相反,未检查的强制转换不会在运行时检查类型安全。

这是一个基于第三版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&lt;Integer&gt;。在现实世界的应用中,这种情况的后果可能是……嗯,很严重。如果使用检查型强制转换,则可以尽早发现类型不匹配。


为避免未经检查的强制转换警告,如果程序员确实确定该方法实际上是安全的,则可以使用@SuppressWarnings("unchecked")。更好的选择是尽可能使用泛型和检查型强制转换。

正如约书亚·布洛赫所说,

...未经检查的警告很重要。不要忽视它们。


为了完整起见,this 回答涉及 Eclipse 细节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2017-04-22
    相关资源
    最近更新 更多