[英]What is the best way to perform a null check WITHOUT the NullPointerException being thrown?
所以我知道我可能有一个空列表(特别是ArrayList)。 现在,当我已经对其进行检查时,像这样的简单检查实际上会引发NullPointerException。 自从我一直成功使用它以来,我就很困惑,但是我确定我缺少一些东西:
public class MyPost {
private int id;
private List<Label> labels;
public MyPost(int id){ this.id = id }
//getter setters for both plus this added method:
public void addLabel(Label aLabel)
{
if(labels == null)
labels = new ArrayList<Label>();
labels.add(aLabel);
}
}
现在,在我的代码的另一部分中,我将遍历客户端发送的ID列表。 为简单起见,假定循环变量“ i”提供了ID
MyPost aPost = new MyPost(i);
按照我的逻辑,我可能会或可能不会在帖子中添加标签。 因此,在进行下一步之前,我先检查是否存在这样的标签:
if(aPost.getLabels()!=null)
//process labels
现在,如果没有在标签列表中添加任何内容,则抛出空指针异常! 但这正是我要检查的内容,并且仍在获得NPE !!!
我知道aPost.getLabels()如果没有添加任何内容,则为null。 但是比较似乎失败并抛出了NPE。 如何解决这个问题? 只是让我感到难过!
更新:这是获取标签代码。 只是一个小小的吸气剂...
public List<Label> getLabels() { return labels;}
我们注意到了我们之前忽略的东西。 我确定Java曾经“短路”它是if条件,即在OR条件中,如果第一个条件评估为true,则它不会检查第二个条件(如果第一个条件评估为false,则类似的AND短路)。 我不确定这是否是原因,但是这是if子句:
if(aPost.getLabels()!=null || !aPost.getLabels().isEmpty())
//process labels
如果列表确实为空,则短路不应该评估第二个条件,对吗? 看来这可能是原因,但我们仍在测试中。 现在只是预感...
通常,调试NPE时要做的第一件事是仔细检查堆栈跟踪并确定从中抛出的确切行。
下一步是检查该行上解引用运算符( .
) 左侧的所有值。 NPE的另一个来源是for
循环的新形式,第三个是自动拆箱,据我所知,没有其他固有地抛出NPE的构造,尽管当然总有代码可以明确地抛出NPE。 。
所有这些意味着没有堆栈跟踪和完整的代码,我们也只能猜测。
(或者,如果您使用的是IDE,则可以简单地设置一个异常断点,并在抛出NPE时检查变量的运行时值。但是,您应该能够通过脱机分析代码和查找NPE来查找NPE。堆栈跟踪。这是一项重要技能。)
更新 :查看更新的问题,很显然if语句是错误的。 它应显示为:
if(aPost.getLabels()!=null && !aPost.getLabels().isEmpty())
//process labels
OR那里不是正确的操作,因为您希望aPost.getLabels()
不为null且不为空。 Java确实会在知道值后立即停止布尔表达式求值,但是在您的原始表达式中,如果aPost.getLabels()
为null,则不是这种情况。
更好的编码方式是始终初始化数组,而不是进行空检查,而只是遍历列表,在空情况下,因为空(不为空),所以什么也不做。
public class MyPost {
private int id;
private List<Label> labels = new ArrayList<Label>;
public MyPost(int id){ this.id = id }
//getter setters for both plus this added method:
public void addLabel(Label aLabel) {
labels.add(aLabel);
}
}
// then later...
public void someProcessing() {
for (Label label: labels) {
// process label here
}
}
现在,您不再需要NPE,并且不必再麻烦地查找null检查代码,您只需依靠一个事实,那就是空列表不会迭代。
另外(如我在评论中所述),如果您必须延迟实例化List,请执行此操作,但始终将可迭代的有效List对象更改为getLabels(),
public List<Label> getLabels() {
return labels == null ? Collections.emptyList() : labels
}
这意味着调用getLabels()的方法永远不需要检查空对象,它们可以简单地遍历返回的对象,这大概是最终您将对列表所做的事情。 这确实确实提高了代码的可靠性和可读性。 我必须回顾许多使用这种束腰缠腰的方法的代码,这些方法总是在访问对象之前检查空值,可以通过确保返回的对象充当其名称的目的(而不是可能为空)来清除所有代码。
编辑:OP更新了有关使用的实际if
语句的帖子后,删除了有关getLabels()的部分,并添加了有关使List充当List的注释。
您实际上缺少了一些东西,尽管您非常接近想要的东西:
在以下形式的表达式中抛出空指针异常
A.method()
A.field
如果A为null。 这意味着在类似
a.b.c.d.e().f.g
如果a为null或ab为null或abc为null,则抛出null指针异常,依此类推:点左侧的内容为null。
因此,在您的示例中,如果您在执行操作时遇到异常
if(aPost.getLabels()!=null)
唯一的解决方案是aPost为null。 没有其他的。
而且实际上您是对的,要知道某项是否为null,最好使用等号(==)将其与null进行比较
将以下内容添加到您的代码中:
if( aPost == null )
System.out.println( "Oh, aPost is null itself and my bug is not related to its fields being null." );
问候,斯特凡
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.