10

当开发人员与非通用 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这里,反正没有其他选择”以及在哪里
  • 结果代码的行为实际上与我们忽略(禁用)警告的代码不同(并且更安全)。

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

更新

前面的例子实际上并没有引起警告。有些答案现在不再有意义。带来不便敬请谅解。

4

3 回答 3

5

根据Item 24of Effective Java 2nd Edition,广泛而频繁地使用@SupressWarnings通常是个坏主意,特别是如果您将此注释应用于整个类,因为此类警告显示您可能有危险的代码片段,这可能会导致ClassCastException.

但在某些情况下它可能很有用,例如在ArrayListtoArray方法实现中:

@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}
于 2013-02-05T12:23:03.593 回答
2

From Effective Java 2nd Edition:

The SuppressWarnings annotation can be used at any granularity, from an individual local variable declaration to an entire class. Always use the SuppressWarnings annotation on the smallest scope possible. Typically this will be a variable declaration or a very short method or constructor. Never use SuppressWarnings on an entire class. Doing so could mask critical warnings.

If you find yourself using the SuppressWarnings annotation on a method or constructor that’s more than one line long, you may be able to move it onto a local variable declaration. You may have to declare a new local variable, but it’s worth it.

It is illegal to put a SuppressWarnings annotation on the return statement, because it isn’t a declaration [JLS, 9.7]. You might be tempted to put the annotation on the entire method, but don’t. Instead, declare a local variable to hold the return value and annotate its declaration

于 2013-02-08T14:22:08.507 回答
2

您的第二个示例不会在我的 Eclipse 中导致编译器警告,我也想不出它应该出现的原因。因此,这是我的首选解决方案。

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

检查普通类型转换,即ClassCastException如果值与所需类型不兼容,它们会导致 a。未经检查的强制转换不能保证这一点,即即使值不是正确的类型,它们也能成功。这可能导致变量持有的值不是其声明类型的子类型,Java 规范称为“堆污染”的情况。为了确保运行时类型系统的完整性,每当使用泛型类型的变量时,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在源代码中不包含强制转换的行处抛出 a 。这可能会严重混淆大多数程序员。

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

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

    private List<T> inhabitants;

    void add(Object o) {
        inhabitants.add(clazz.cast(o));
    }
}
于 2013-02-05T12:38:11.837 回答