Unexpected behavior when creating Scala Option from java.lang.Long
Asked Answered
B

1

6

Given following code:

val javaLong: java.lang.Long = null
val opt: Option[Long] = Option(javaLong)

I expected opt to be None but for some reason it is Some(0). I also found this bug and it appears that implicit conversion happens before option's apply method. I think this is still a bug because one wouldn't expect that behavior and I am wondering is there any workaround or some better ways to wrap nulls.

Update: The code above is only a simplified piece. The real example is something like:

Person(Option(doc.getLong()))

where Person is:

 case class Person(id: Option[Long])

and method doc.getLong() is java method that returns java.lang.Long

Belfry answered 10/10, 2018 at 16:9 Comment(0)
I
3

The conversion to Option[Long] is actually not just a conversion to an Option but also an implicit cast to Scala's Long from Java's Long.

val javaLong: java.lang.Long = null
// javaLong: Long = null

val long: java.lang.Long = javaLong
// long: Long = null
val long: Long = javaLong
// long: Long = 0

First we have the implicit conversion from Java's Long to Scala's Long which apparently produces 0 out of null.

Only then do we have the conversion to an Option.

If we specify Java's Long as a type of our Option then we do get None and not Some(0):

val opt: Option[java.lang.Long] = Option(javaLong)
// opt: Option[Long] = None (here it's Java's Long)

And we can finally cast the Option[java.lang.Long] to use Scala's Long:

val opt: Option[Long] = Option(javaLong).map(_.toLong)
// opt: Option[Long] = None (here it's Scala's Long)
Incarcerate answered 10/10, 2018 at 16:40 Comment(6)
Yes, but the problem appears when it used in single line code in constructor. I updated the questionBelfry
You might want to create an intermediate variableIncarcerate
I think it won't help. val l:Long = javaLong will produce 0. And Option[java.lang.Long] is not the same as Option[Long]Belfry
You can map the resulting Option[java.lang.Long] to Scala's Long: Option(javaLong).map(_.toLong). Person(Option(javaLong).map(_.toLong)) results in Person(None)Incarcerate
Thanks.map(_.toLong) works for me. But anyway this is strange behavior of Option :)Belfry
Option is working as expected, the oddity is expecting java.lang.Long and Long to be the same when one can be null and the other can't! You would have the same problem with any generic that tested for null values.Fortyish

© 2022 - 2024 — McMap. All rights reserved.