What is the best way to perform a null check WITHOUT the NullPointerException being thrown?
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 t开发者_如何学Pythono 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...
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.
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.
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
精彩评论