Home > Back-end >  Scala null pointer exception cause by .toString on a variable which is not null
Scala null pointer exception cause by .toString on a variable which is not null

Time:06-09

Can anyone please explain the following behavior in scala?

Basically in the following piece of code, if you do getOptionValue[Int](None), it returns 0 but when you do getOptionValue[Int](None).toString it causes a null pointer exception.

Shouldn't the outcome be 0 cast into a string (ie. "0")?

PS: I am using scala 2.12.10

scala> def getOptionValue[T](option: Option[T]): T = {
     |     if (option.isEmpty) {
     |       null.asInstanceOf[T]
     |     } else
     |       option.get
     |   }
getOptionValue: [T](option: Option[T])T

scala> getOptionValue[Int](None)
res35: Int = 0

scala> res35.toString
res36: String = 0

scala> getOptionValue[Int](None).toString
java.lang.NullPointerException
  ... 47 elided

Also the below works just fine

scala> null.asInstanceOf[Int]
res0: Int = 0

scala> null.asInstanceOf[Int].toString
res1: String = 0

CodePudding user response:

These:

null.asInstanceOf[Int]
null.asInstanceOf[Int].toString // "0"

are rather (unreliable) exceptions.

On JVM calling any method on null reference ends up with NullPointerException.

Scala doesn't treat scala.Int always as java.lang.Integer (which can be nullable) nor it always treat it as int (primitive, non-nullable by design) - it uses either representation based on context.

In case of null.asInstanceOf[Int] Scala will actually perform a conversion rather than casting, which is why you'll get 0. But it can only perform this casting when it knows the cast type (Null) and the target type (Int), as after the type-erasure it doesn't do such tricks.

Which is why when you are using generics, Scala will only know that there is null cast to some reference type T, method definition doesn't know anything about T being Int, so it won't perform conversion, it will still be null returned from the method, and then it will throw NPE on .toString.

The reason these exceptions exist in the first place is that it probably felt bad that when you have case like:

val a: Int = null.asInstanceOf[Int]

compiler would most likely use int, casting wouldn't make sense, so they opted for conversion so that primitive would get the value 0 rather than compile or runtime error. But this can only be done when you know that you are targeting something that can be a primitive type (Int, Double, Char, ...).

  • Related