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
, ...).