ITEM 27: ELIMINATE UNCHECKED WARNINGS
使用泛型编程时,您将看到许多编译器警告: 未经检查的强制转换警告、未经检查的方法调用警告、未经检查的参数类型警告和未经检查的转换警告。使用泛型获得的经验越多,得到的警告就越少,但是不要期望新编写的代码能够干净地编译。
许多未经检查的警告很容易消除。例如,假设您不小心编写了以下声明:
Set<Lark> exaltation = new HashSet();
编译器会温柔地提醒你做错了什么:
“Venery.java:4: warning: [unchecked] unchecked conversion Set<Lark> exaltation = new HashSet(); required: Set<Lark> found: HashSet”
我们应该按照指示更正,使警告消失。注意,实际上不需要指定类型参数,只需要指出它与在 Java 7 中引入的 diamond 操作符 (<>) 一起出现。编译器将推断正确的实际类型参数(在本例中为Lark):
Set<Lark> exaltation = new HashSet<>();
有些警告更难消除。本章充满了此类警告的例子。当你得到需要一些思考的警告时,坚持下去!尽可能消除所有未经检查的警告。如果消除了所有警告,就可以确保代码是类型安全的,这是一件非常好的事情。这意味着您不会在运行时获得ClassCastException,并且它增加了您的信心,使您相信您的程序将按照您的意愿运行。
如果不能消除警告,但是可以证明触发警告的代码是 typesafe,那么(而且只有在这种情况下)使用 @SuppressWarnings(“unchecked”) 注释来抑制警告。如果您在没有首先证明代码是类型安全的情况下就抑制了警告,那么您就给了自己一种错误的安全感。代码可以在编译时不发出任何警告,但仍然可以在运行时抛出ClassCastException。但是,如果忽略未检查的安全警告(而不是抑制它们),就不会注意到什么时候出现了一个表示真正问题的新警告。新的警告将在你没有保持沉默的所有错误警报中消失。
SuppressWarnings 注释可以用于任何声明,从单个局部变量声明到整个类。始终在尽可能小的范围内使用 SuppressWarnings 注释。通常这将是一个变量声明或一个非常短的方法或构造函数。永远不要在整个类上使用 SuppressWarnings。这样做可能会掩盖严重的警告。
如果您发现自己在一个方法或构造函数上使用了 SuppressWarnings 注释,并且该注释的长度超过一行,那么您可以将它移动到一个局部变量声明上。您可能需要声明一个新的局部变量,但这是值得的。例如,考虑这个 toArray 方法,它来自 ArrayList:
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[]”
在 return 语句上放置一个 SuppressWarnings 注释是非法的,因为它不是一个声明[JLS, 9.7]。您可能想把注释放在整个方法上,但是不要这样做。相反,声明一个局部变量来保存返回值并注释它的声明,如下所示:
// 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”) 注释在尽可能小的范围内禁止警告。在评论中记录下你决定取消警告的理由。
网友评论