• Post Reply Bookmark Topic Watch Topic
  • New Topic

That's one very tricky NullPointerException!  RSS feed

 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Consider the following code snippet which is a simplified version of an actual scenario I encountered today while working on an actual projectWhen you execute this code snippet, a NullPointerException is thrown. Here is the actual output:At first I was a bit astonished that a NullPointerException was thrown on line8. I didn't expect that at all as the main purpose of the toInteger method is to have a null-safe conversion from String to Integer. After looking at the code snippet more closely I discovered the reason why. But I won't share the reason because I think it's a great pop quiz question and I don't want to spoil your fun of discovering the reason yourself

Kind regards,
Roel
 
Dave Tolls
Ranch Foreman
Posts: 3056
37
  • Likes 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Just to check if I my first idea is right, I'm guessing that using valueOf instead of parseInt doesn't result in an NPE?
 
Tim Cooke
Marshal
Posts: 4039
239
Clojure IntelliJ IDE Java
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's a good one Roel. I've learnt something new today

I'll keep the suspense going too.
 
Henry Wong
author
Sheriff
Posts: 23295
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Interesting. I was under the impression that this should not throw an NPE with Java 7 and later... so, perhaps, I missed the trick.

Henry
 
Maor Pau
Greenhorn
Posts: 11
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
is it because of the ternary is written wrong ?
 
Mohnish Saini
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
ya this one is trickier. NPE is there because ternary operator always return same data type.

return string == null ? defaultValue : Integer.parseInt(string);

here, compiler tries to parse defaultValue (which is null) into Integer using Integer.parseInt(defaultValue) which causes NPE.

Thanks
Mohnish
 
Paul Anilprem
Enthuware Software Support
Ranch Hand
Posts: 4114
34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Good one, Roel
 
salvin francis
Bartender
Posts: 1649
37
Eclipse IDE Google Web Toolkit Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have to confess, I don't know whats the reason for the NPE. I could have sworn there was no possibility of a NPE. Will have to try this one and evaluate why.
 
praveen kumaar
Ranch Hand
Posts: 461
22
Android Chrome Eclipse IDE Google App Engine Java Notepad Oracle Ubuntu Windows
  • Likes 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mohnish Saini wrote:...here, compiler tries to parse defaultValue (which is null) into Integer using Integer.parseInt(defaultValue) which causes NPE.

Do you really think compiler tries to parse
that's really a trickier.
as a hint one can read the specs:
Java® Language Specification wrote:The type of a conditional expression is determined as follows:

. If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.

. If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

. If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type....


related with unboxing...
 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
is it because of the ternary is written wrong ?

No, it definitely isn't! I know my Java very well
 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mohnish Saini wrote:ya this one is trickier. NPE is there because ternary operator always return same data type.

That's indeed a part of the story.

Mohnish Saini wrote:here, compiler tries to parse defaultValue (which is null) into Integer using Integer.parseInt(defaultValue) which causes NPE.

First of all, the compiler has only 1 task (which it does excellent) and that is: compiling your code and check if it doesn't violate any of the rules mentioned in the JLS (and complains about it when it notices a violation giving you a compiler error). So the compiler definitely doesn't try to parse defaultValue. That happens at runtime and is done by the JVM.
Secondly, defaultValue is already of type Integer, so there is no need to parse defaultValue into Integer.
 
Ramya R Subramanian
Ranch Hand
Posts: 182
18
  • Likes 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
My assumption based on this point from JLS:

If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

Third operand type is (Integer.parseInt(String))  int which is Primitive type
The type of the second is got by applying the boxing conversion to int - Integer

So the resultant type of the conditional expression is int(primitive type) and then converted to Integer.Something like this happens

(Auto unboxing)defaultValue.intValue() gives the NPE. Using primitive types with ternary operator seems to be the best option to avoid such confusions.

 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
praveen kumaar wrote:as a hint one can read the specs:
Java® Language Specification wrote:The type of a conditional expression is determined as follows:

. If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

That's indeed the reason why the NullPointerException is thrown! :thumb-up: Have a cow for going through the JLS and finding the appropriate set of rules (that saves me some work )

Because the third operand of the conditional expression (Integer.parseInt(string)) evaluates to int and the second operand of the conditional expression (defaultValue) evaluates to Integer which is the wrapper class of int, the type of the conditional expression is int. That means that line8 of the code snippet looks behind the scenes like thisAnd because the first operand of the conditional expression (string == null) evaluates to true, the JVM tries to unbox defaultValue to int using defaultValue.intValue(), causing a NullPointerException to be thrown (as defaultValue is null).

Finally I like to note that it's quite hard (if not nearly impossible) to debug this line of code to find the root cause of the NullPointerException being thrown, because it's not thrown from an actual statement in your code, but from a statement behind the scenes. And that makes it really hard. And if you are totally flabbergasted by this NullPointerException and you try to rewrite line8 so the conditional expression is not used like this to see what's causing the NullPointerExceptionYou will not get a NullPointerException at all

If you want to use the conditional expression without the NullPointerException being thrown, you must use valueOf instead of parseInt (as suggested by Dave, have a cow). So if you replace line8 withthe type of the conditional expression is Integer (both second and third operands evaluate to Integer) and therefor there's no need to unbox defaultValue (which is null) and thus no NullPointerException is thrown.

That's why this bug could be very time-consuming to fix. Luckily I knew my Java inside out (thanks to preparing for many Java OCP certifications ) and decided to share on my favourite (Java) website, so others would not and throw their computer/laptop through the window

Hope it helps!
Kind regards,
Roel
 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ramya Subraamanian wrote:My assumption based on this point from JLS

Your assumption is absolutely spot-on Have a cow!

Ramya Subraamanian wrote:Using primitive types with ternary operator seems to be the best option to avoid such confusions

Unfortunately that was not possible in this (utility) method according to the requirements: it must be able to return null as a default value.

I would say the best option to avoid confusion is: make sure both second and third operands of the conditional expression evaluate to the same type. And in this particular method that can easily be achieved by replacing parseInt with valueOf (as mentioned in my previous post).
 
Dave Tolls
Ranch Foreman
Posts: 3056
37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roel De Nijs wrote:And in this particular method that can easily be achieved by replacing parseInt with valueOf (as mentioned in my previous post).


See my post at the top...;)

But that's mainly because I encountered just this thing many moons ago, and it's stuck with me since.
It was one of the classic gotchas of autoboxing/unboxing back when it first appeared, IIRC.
 
Roel De Nijs
Sheriff
Posts: 11338
177
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Dave Tolls wrote:See my post at the top...;)

I was aware of your post and gave you credit in my post before that one. And even awarded you a cow!
Roel De Nijs wrote:If you want to use the conditional expression without the NullPointerException being thrown, you must use valueOf instead of parseInt (as suggested by Dave, have a cow).
 
Martijn Dwars
Greenhorn
Posts: 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Fun fact: the same bug caught me by surprise a couple of years ago, except the direction of the boxing was the other way around. The code pretty much looked like:



Line 5 was throwing an NPE. Why? Hashtable.get returns null when the key does not exist, and unboxing null to a primitive type is not possible.
 
praveen kumaar
Ranch Hand
Posts: 461
22
Android Chrome Eclipse IDE Google App Engine Java Notepad Oracle Ubuntu Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to CodeRanch!!

Why were you surprised there as it is too clearly mentioned in the API that "get" will throw the NullPointerException if the specified key is null.
 
Martijn Dwars
Greenhorn
Posts: 2
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
praveen kumaar wrote:Welcome to CodeRanch!!

Why were you surprised there as it is too clearly mentioned in the API that "get" will throw the NullPointerException if the specified key is null.


Actually, the API mentions that get "returns null if this map contains no mapping for the key." Because the return type is a primitive integer, the JVM tries to unbox the null value to the primitive type int, which gives an NPE.
 
praveen kumaar
Ranch Hand
Posts: 461
22
Android Chrome Eclipse IDE Google App Engine Java Notepad Oracle Ubuntu Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
actually what have you said earlier
Martijn wrote:..Hashtable.get returns null when the key does not exist...
is different from whatever have you said now
Martijn wrote:returns null if this map contains no mapping for the key.
Indeed your wordings is even more trickier than the roel's NPE..
Anyway 
Kind regards,
Praveen.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!