执行空检查而不引发 NullPointerException 的最佳方法是什么?

发布于 2024-11-27 14:23:33 字数 1273 浏览 1 评论 0原文

所以我知道我可能有一个空列表(特别是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”提供 ids

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条件中,如果第一个条件评估为真,则不会检查第二个条件(如果第一个条件评估为假,则AND的类似短路)。我不完全确定这是否是原因,但这里是 if 子句:

if(aPost.getLabels()!=null || !aPost.getLabels().isEmpty())
//process labels

如果列表确实为空,短路不应评估第二个条件,对吗?看来这可能是原因,但我们仍在测试中。现在只是一个预感...

So I know I MAY have a null List (ArrayList, specifically). Now a simple check for it like that actually throws a NullPointerException when I'm already checking for it. This one has me stumped since I've always used it successfully but I'm sure I'm missing something:

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);
  }

}

Now in another part of my code I am iterating over the list of ID's sent by the client. For simplicity assume that a loop variable 'i' is supplying the ids

MyPost aPost = new MyPost(i);

In my logic I may or may not add labels to the post. So at the end before proceeding I check for existence of labels like that:

if(aPost.getLabels()!=null)
   //process labels

Now this throws a null pointer exception if nothing was ever added to the list of labels! But that's exactly what I want to check for and am still getting an NPE!!!

I know aPost.getLabels() IS null if nothing is ever added to it. But the comparison seems to fail and throw an NPE. How to address this issue? Just has me stumped!

UPDATE:
Here is the get label code. Just a trivial getter...

public List<Label> getLabels() { return labels;}

We noticed something that we overlooked before. I'm sure java used to "short circuit" it's if-conditions i.e., in an OR condition if the first condition evaluated to true it wouldn't check the second one (similar short-circuiting for AND if the first condition evaluated to false). I am not entirely sure if that is the cause but here's the if-clause:

if(aPost.getLabels()!=null || !aPost.getLabels().isEmpty())
//process labels

If the list is indeed null short-circuiting shouldn't evaluate the second condition, correct?? It seems that may be the cause, but we are still testing it out. Just a hunch for now...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

﹎☆浅夏丿初晴 2024-12-04 14:23:33

一般来说,调试 NPE 时要做的第一件事是仔细检查堆栈跟踪并确定抛出它的确切行。

下一步是检查该行解引用运算符 (.)左侧的所有值。 NPE 的另一个来源是 for 循环的新风格,第三个来源是自动拆箱,据我所知,没有其他结构本身会抛出 NPE,尽管当然可以始终是显式抛出它的代码。

所有这一切意味着,如果没有堆栈跟踪和完整的代码,我们也只能猜测。

(或者,如果您使用 IDE,您可以简单地设置一个异常断点,并在抛出 NPE 时检查变量的运行时值。但是您应该能够通过离线分析代码来找到 NPE,并且堆栈跟踪。这是一项重要的技能。)

更新:查看更新的问题,很明显 if 语句是错误的。它应该是:

if(aPost.getLabels()!=null && !aPost.getLabels().isEmpty())
//process labels

OR 不是正确的操作,因为您希望 aPost.getLabels() 不为 null 并且不为空。 Java 确实会在知道值后立即停止布尔表达式计算,但在原始表达式中,如果 aPost.getLabels() 为 null,则情况并非如此。

In general the first thing you do when you debug an NPE is to examine the stack trace closely and identify the exact line it is thrown from.

The next step is to check all the values that are on the left side of a dereferencing operator (.) on that line. Another source of NPEs is the new flavour of for loops, and the third one is auto-unboxing, As far as I know, there are no other constructs that inherently throw an NPE, although of course there can always be code that throws it explicitly.

All this means that without the stack trace and the complete code we can only guess too.

(Alternatively, if you're using an IDE you can simply set an exception breakpoint and examine the runtime values of your variables at the moment the NPE is being thrown. But you should be able to find an NPE by offline analysis of the code and the stack trace. It's an important skill.)

Update: Looking at the updated question it's obvious that the if statement is wrong. It should read:

if(aPost.getLabels()!=null && !aPost.getLabels().isEmpty())
//process labels

OR isn't the correct operation there, since you want aPost.getLabels() to be not null AND not to be empty. Java does indeed stop boolean expression evaluation as soon as the value is known, but in your original expression this wasn't the case if aPost.getLabels() was null.

灯角 2024-12-04 14:23:33

更好的编码方法是始终初始化数组,而不是简单地迭代列表进行空检查,在空的情况下,列表不会执行任何操作,因为它是空的(不是空的)。

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,并且您不必拥有看起来令人讨厌的空检查代码,您只需依赖于空列表不会迭代的事实。

或者(正如我在评论中所说),如果您必须延迟实例化 List,请执行此操作,但始终返回一个可以迭代的有效 List 对象,方法是将 getLabels() 更改为

public List<Label> getLabels() {
    return labels == null ? Collections.emptyList() : labels
}

这意味着没有调用 getLabels() 的方法当需要检查空对象时,他们可以简单地迭代返回的对象,这大概就是您最终将对列表执行的操作。这确实提高了代码的可靠性和可读性。我必须审查很多具有这种腰带和护腕方法的代码,在访问对象之前始终检查空值,这可以通过确保返回的对象按照其名称而不是可能为空来清理。

编辑:在OP更新有关实际使用的 if 语句的帖子后删除有关 getLabels() 的部分,并添加有关使列表充当列表的注释。

A better way to code it would be to always initialise the array, and instead of null checking simply iterate over the list, which in the empty case would do nothing because it's empty (not 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
    }
}

Now you no longer have a NPE, and you're not having to have nasty looking null checking code, you simply rely on the fact an empty list won't iterate.

Alternatively (as I say in my comments) if you have to lazy instantiate the List, do that but always return a valid List object that can be iterated over, by changing the getLabels() to

public List<Label> getLabels() {
    return labels == null ? Collections.emptyList() : labels
}

This means that no method that calls getLabels() ever need check for a null object, they can simply iterate over the returned object, which is presumably what you'll end up doing to the list eventually. This really does improve code reliability, and readability. I have to review a lot of code that has this belt and bracers approach of always checking for nulls before accessing objects, which could all be cleaned up by ensuring the returned object acts as its name sake instead of potentially being null.

EDIT: remove the section about the getLabels() after OP updated post about the actual if statement used, and added the comments about making the List act as a List.

林空鹿饮溪 2024-12-04 14:23:33

尽管您非常接近您想要的,但您实际上错过了一些东西:

在以下形式的表达式中抛出空指针异常

A.method()

A.field

如果 A 为空,则 。这意味着在像空指针这样的语句中,

a.b.c.d.e().f.g 

如果 a 为 null 或 ab 为 null,或者 abc 为 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." );

问候,
史蒂芬

You are actually missing something, although you are very close to what you want :

a null pointer exception is thrown in an expression of the form

A.method()

A.field

if A is null. This means that in a statement like

a.b.c.d.e().f.g 

a null pointer exception is thrown if either a is null or a.b is null, or a.b.c is null, and so on : something at the left hand side of a dot is null.

So in you example, if you get an exception when doing

if(aPost.getLabels()!=null)

the only solution is that aPost is null. Nothing else.

And actually you are right, to know if something is null, it's good to compare it to null using equal sign (==)

Add the following to your code :

if( aPost == null )
   System.out.println( "Oh, aPost is null itself and my bug is not related to its fields being null." );

Regards,
Stéphane

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文