Java 为什么不干脆禁用未检查的警告?
当开发人员与非通用API交互时,他们通常会遇到“未检查”的警告。考虑下面的例子:Java 为什么不干脆禁用未检查的警告?,java,generics,unchecked,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
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();
}
}
import java.util.AbstractList;
导入org.w3c.dom.Node;
导入org.w3c.dom.NodeList;
公共类IterableNodeList扩展了AbstractList
{
私有节点列表;
公共IterableNodeList(节点列表)
{
this.list=列表;
}
公共T获取(整数索引)
{
返回(T)该列表项(索引);
}
公共整数大小()
{
返回此.list.getLength();
}
}
当然,我们可以投入精力,以这样一种方式编写它:在类上使用类型参数T
,并使用构造函数参数class
,匹配成员变量和cast()
调用
或者,可以考虑简单地编辑IDE配置和构建脚本(例如Maven POM),以完全禁用此编译器警告。现在,如果我们这样做,代码可以保持原样,但我确信这样做肯定有缺点。然而,我想不出任何合理、现实的例子
- 此警告提供的价值大于“在此处粘贴
,反正没有其他选项”,以及@SuppressWarnings
- 结果代码的行为实际上与我们忽略(禁用)警告的代码不同(更安全)
前面的例子实际上并没有引起警告。有些答案现在不再有意义了。很抱歉给您带来不便。根据
有效Java第二版
的第24项
,广泛而频繁地使用@superswarnings
通常是一个坏主意,特别是如果您将此注释应用于整个类,因为此类警告显示您的代码中可能存在危险的部分,这可能导致ClassCastException
但在某些情况下,它可能很有用,例如在ArrayList
的toArray
方法实现中:
@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;
}
@SuppressWarnings(“未选中”)
公共T[]toArray(T[]a){
如果(a.长度<尺寸)
//创建a的运行时类型的新数组,但我的内容:
return(T[])array.copyOf(elementData,size,a.getClass());
System.arraycopy(elementData,0,a,0,size);
如果(a.长度>尺寸)
a[size]=null;
返回a;
}
您的第二个示例不会在我的eclipse中导致编译器警告,我也想不出它应该出现的原因。因此,这是我首选的解决方案
存在未经检查的警告的原因是忽略它们会导致堆污染:
检查普通类型转换,即如果值与所需类型不兼容,则会导致ClassCastException
。未选中的强制转换不能保证成功,即即使值的类型不正确,也可以成功。这可能导致一个变量包含的值不是其声明类型的子类型,这是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);
}
摘自Java第二版:
SuppressWarnings
注释可以从
对整个类的单个局部变量声明始终在尽可能小的范围内使用SuppressWarnings
注释。通常情况下,这将是一个
变量声明或非常短的方法或构造函数。切勿对整个类使用SuppressWarnings
。这样做可能会掩盖严重警告
如果您发现自己在方法上使用SuppressWarnings
注释,或
如果构造函数的长度超过一行,则可以将其移动到本地
变量声明。您可能需要声明一个新的局部变量,但这是值得的
它
在return语句上放置SuppressWarnings
注释是非法的,
因为它不是一个声明[JLS,9.7]。您可能会尝试放置注释
在整个方法上,但不要。相反,声明一个局部变量来保存
返回值并注释其声明
我的问题是要得到这些“可能是危险的代码片段”的例子,因为我想不出一个。如果我们不能给出编译器发出此类警告的好例子,我们可以使用构建脚本/IDE(Maven,eclipse)设置禁用它们,然后甚至不必到处添加@SuppressWarnings@JensBannmann查看我的答案,并向下滚动到“关于堆污染的说明”以查看危险代码的示例。@PaulBellora好的,这是一个很好的答案-尤其是“在调用站点而不是方法中爆炸”部分。你能在这里发表你的评论作为答案吗,或者甚至把“关于堆污染的注释”部分复制到答案中?目前我的问题没有一个直接的答案,我认为如果有,对其他人可能会有用。@JensBannmann实际上非常接近同一点:“程序……在源代码中不包含强制转换的行中抛出ClassCastException。这可能会严重迷惑大多数程序员。”但我会在有机会的时候考虑把这一部分变成答案。这个问题其实更合适。@PaulBellora:我又一次被这个问题绊倒了。请在这里将您的堆污染注释转换为答案:-)在示例中,我没有处理键入的列表,我现在已经使这一点更加明显。我的第二个示例确实引发了编译器警告,第三个则没有。但是,在第三个例子中,我们得到了什么
class Habitat<T> {
private final Class<T> clazz;
private List<T> inhabitants;
void add(Object o) {
inhabitants.add(clazz.cast(o));
}
}