Effective Java 第三版——27. 消除非检查警告

Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必不少人都读过,号称Java四大名著之一,不过第二版2009年出版,到如今已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深入的变化。
在这里第一时间翻译成中文版。供你们学习分享之用。java

Effective Java, Third Edition

27. 消除非检查警告

使用泛型编程时,会看到许多编译器警告:未经检查的强制转换警告,未经检查的方法调用警告,未经检查的参数化可变长度类型警告以及未经检查的转换警告。 你使用泛型得到的经验越多,得到的警告越少,但不要指望新编写的代码可以干净地编译。编程

许多未经检查的警告很容易消除。 例如,假设你不当心写了如下声明:安全

Set<Lark> exaltation = new HashSet();

编译器会提醒你你作错了什么:学习

Venery.java:4: warning: [unchecked] unchecked conversion
        Set<Lark> exaltation = new HashSet();
                               ^
  required: Set<Lark>
  found:    HashSet

而后能够进行指示修正,让警告消失。 请注意,实际上并不须要指定类型参数,只是为了代表它与Java 7中引入的钻石运算符("<>")一同出现。而后编译器会推断出正确的实际类型参数(在本例中为Lark):ui

Set<Lark> exaltation = new HashSet<>();

但一些警告更难以消除。 本章充满了这种警告的例子。 当你收到须要进一步思考的警告时,坚持不懈! 尽量地消除每个未经检查的警告。 若是你消除全部的警告,你能够放心,你的代码是类型安全的,这是一件很是好的事情。 这意味着在运行时你将不会获得一个ClassCastException异常,而且增长了你的程序将按照你的意图行事的信心。翻译

若是你不能消除警告,但你能够证实引起警告的代码是类型安全的,那么(而且只能这样)用@SuppressWarnings(“unchecked”)注解来抑制警告。 若是你在没有首先证实代码是类型安全的状况下压制警告,那么你给本身一个错误的安全感。 代码可能会在不发出任何警告的状况下进行编译,可是它仍然能够在运行时抛出ClassCastException异常。 可是,若是你忽略了你认为是安全的未经检查的警告(而不是抑制它们),那么当一个新的警告出现时,你将不会注意到这是一个真正的问题。 新出现的警告就会淹没在全部的错误警告当中。code

SuppressWarnings注解可用于任何声明,从单个局部变量声明到整个类。 始终在尽量最小的范围内使用SuppressWarnings注解。 一般这是一个变量声明或一个很是短的方法或构造方法。 切勿在整个类上使用SuppressWarnings注解。 这样作可能会掩盖重要的警告。blog

若是你发现本身在长度超过一行的方法或构造方法上使用SuppressWarnings注解,则能够将其移到局部变量声明上。 你可能须要声明一个新的局部变量,但这是值得的。 例如,考虑这个来自ArrayList的toArray方法:ip

public <T> T[] toArray(T[] a) {
    if (a.length < size)
       return (T[]) Arrays.copyOf(elements, size, a.getClass());
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size)
       a[size] = null;
    return a;
}

若是编译ArrayList类,则该方法会生成此警告:
ArrayList.java:305: warning: [unchecked] unchecked cast
       return (T[]) Arrays.copyOf(elements, size, a.getClass());
                                 ^
  required: T[]
  found:    Object[]

在返回语句中设置SuppressWarnings注解是非法的,由于它不是一个声明[JLS,9.7]。 你可能会试图把注释放在整个方法上,可是不要这要作。 相反,声明一个局部变量来保存返回值并标注它的声明,以下所示:element

// Adding local variable to reduce scope of @SuppressWarnings
public <T> T[] toArray(T[] a) {
    if (a.length < size) {
        // This cast is correct because the array we're creating
        // is of the same type as the one passed in, which is T[].
        @SuppressWarnings("unchecked") T[] result =
            (T[]) Arrays.copyOf(elements, size, a.getClass());
        return result;
    }
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

所产生的方法干净地编译,并最小化未经检查的警告被抑制的范围。

每当使用@SuppressWarnings(“unchecked”)注解时,请添加注释,说明为何是安全的。 这将有助于他人理解代码,更重要的是,这将减小有人修改代码的可能性,从而使计算不安全。 若是你以为很难写这样的注释,请继续思考。 毕竟,你最终可能会发现未经检查的操做是不安全的。

总之,未经检查的警告是重要的。 不要忽视他们。 每一个未经检查的警告表明在运行时出现ClassCastException异常的可能性。 尽你所能消除这些警告。 若是没法消除未经检查的警告,而且能够证实引起该警告的代码是安全类型的,则能够在尽量小的范围内使用 @SuppressWarnings(“unchecked”)注解来禁止警告。 记录你决定在注释中抑制此警告的理由。

相关文章
相关标签/搜索