为什么不简单地禁用未经检查的警告?

时间:2013-02-05 12:11:29

标签: java generics unchecked

当开发人员与非通用API交互时,他们通常会遇到“未经检查”的警告。请考虑以下示例:

import java.util.AbstractList;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class IterableNodeList<T extends Node> extends AbstractList<T>
{
    private NodeList list;

    public IterableNodeList(NodeList list)
    {
        this.list = list;
    }

    public T get(int index)
    {
        return (T)this.list.item(index);
    }

    public int size()
    {
        return this.list.getLength();
    }
}

当然,人们可以投入努力以这样的方式编写它,即没有警告:在类上使用类型参数T和构造函数参数Class<T>,匹配成员变量和cast()致电。

或者,可以考虑简单地编辑IDE配置和构建脚本(例如Maven POM)以完全禁用此编译器警告。现在,如果我们这样做,代码可以保持原样,但我确信做必须有缺点。但是,我想不出任何合理,现实的例子

  • 此警告提供的价值超过“坚持@SuppressWarnings此处,无论如何都没有其他选项”,以及
  • 生成的代码实际上与我们忽略(禁用)警告的代码不同(且更安全)。

你能想到这样的例子或者说出为什么在全球范围内禁用这些“未经检查”的警告是个坏主意的另一个原因吗?或者这实际上是个好主意?

更新

之前的例子实际上没有引发警告。现在有些答案不再有意义了。很抱歉给您带来不便。

3 个答案:

答案 0 :(得分:5)

根据Item 24的{​​{1}},广泛而频繁地使用Effective Java 2nd Edition通常是个坏主意,特别是如果你将这个注释应用于整个班级,因为这样的警告显示你可能有危险的部分您的代码,可能会导致@SupressWarnings

但在某些情况下,它可能很有用,例如在ClassCastException的{​​{1}}方法实现中:

ArrayList

答案 1 :(得分:2)

你的第二个例子不会在我的日食中产生编译器警告,也不能想出它为什么会这样做的原因。因此,这是我的首选解决方案。

存在未经检查的警告的原因是忽略它们会导致堆污染

检查普通演员表,如果该值与所需类型不兼容,则会导致ClassCastException。未经检查的强制转换不保证,即使值不是正确类型,它们也可以成功。这可能导致变量保存的值不是其声明类型的子类型,这是Java规范calls“堆污染”的条件。为了确保运行时类型系统的完整性,只要使用泛型类型的变量,Java编译器就会插入普通的强制转换。在存在堆污染的情况下,这些铸件会失效。

例如,程序:

static void appendTo(List list) {
    list.add(1); // unchecked warning
}

static void printLengths(List<String> strings) {
    for (String s : strings) { // throws ClassCastException
        System.out.println(s.length());
    }
}

public static void main(String[] args) throws Exception {
    List<String> strings = new ArrayList<>();
    strings.add("hello");
    appendTo(strings);
    printLengths(strings);
}

在源代码中不包含强制转换的行抛出ClassCastException。这可能会使大多数程序员感到非常困惑。

这就是为什么我建议尽可能使用经过检查的强制转换,使用非泛型强制转换,或者(在通用代码中)使用反射强制转换:

class Habitat<T> {
    private final Class<T> clazz;

    private List<T> inhabitants;

    void add(Object o) {
        inhabitants.add(clazz.cast(o));
    }
}

答案 2 :(得分:2)

来自 Effective Java 2nd Edition

  

SuppressWarnings注释可用于任何粒度,来自   单个局部变量声明到整个类。 始终在尽可能小的范围内使用SuppressWarnings注释。通常这将是一个   变量声明或非常短的方法或构造函数。切勿在整个班级上使用SuppressWarnings。这样做可能会掩盖严重警告。

     

如果您发现自己在方法或方法上使用SuppressWarnings注释   构造函数超过一行,您可以将它移动到本地   变量声明。您可能必须声明一个新的局部变量,但它是值得的   它

     

在return语句中放置SuppressWarnings注释是违法的,   因为它不是声明[JLS,9.7]。您可能想要添加注释   关于整个方法,但不要。相反,声明一个局部变量来保存   返回值并注释其声明