Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creation of Option from java.lang values may cause a null pointer exception #9634

Closed
scabug opened this issue Jan 29, 2016 · 5 comments
Closed

Comments

@scabug
Copy link

scabug commented Jan 29, 2016

As expressed in SO 33650809, the creation of Option from a null-carrying java.lang value can cause a NullPointerException to be thrown.

This is because the way Option factory is defined:

def apply[A](x: A): Option[A] = if (x == null) None else Some(x)

What happens is that the x (being e.g. java.lang.Long - and carrying a null) is first implicitly converted to scala.Long, before being compared to null. scala.Long is an AnyVal and cannot thus carry a null. Thus the exception.

I would see two ways to fix this unexpected behaviour, as explained in my proto pull request.

The approach I would like to see in future Scala versions is a guarantee that an Option will never carry a java.lang value, but the corresponding Scala AnyVal. I.e. one could not create an Option[java.lang.Long], at all. This makes sense since the use of Option is often used at Java/Scala borders, to filter out nulls. If the value is not null, we can just as well represent it as a Scala AnyVal. This change would only make the guarantee of Option stronger.

Also, the way an IDE can obscure the difference between scala.Long and java.lang.Long from the user (showing both as simply Long), this would help managing conversions in general. Creating an Option from a java.lang.Long would create an Option[scala.Long], which is what APIs likely would be expecting. Without this change, one gets weird error messages such as "expecting Option[Long] but had Option[Long]".

The other way to solve the NullPointerException problem would be to use two type parameters to Option, allowing the test against null to precede the implicit conversion. This, however, would break code that explicitly provides one (target) template argument for Option. It would also allow Option[java.lang.Long] to be created, leading to the above compilation issues.

@scabug
Copy link
Author

scabug commented Jan 29, 2016

Imported From: https://issues.scala-lang.org/browse/SI-9634?orig=1
Reporter: Asko Kauppi (akauppi)
Affected Versions: 2.11.7, 2.12.0-M3

@scabug
Copy link
Author

scabug commented Jan 29, 2016

@SethTisue said:
PR: scala/scala#4930

@scabug
Copy link
Author

scabug commented Jan 29, 2016

@SethTisue said (edited on Jan 29, 2016 6:57:34 PM UTC):
I don't think this is worth doing just for Option.apply, while leaving the rest of the language and standard library unchanged. It wouldn't be consistent with how Scala handles boxed vs. unboxed types in any other situation, and it would change the meaning of existing code. (Plus it would involve adding overloads on Option.apply, which currently has none, which is desirable; overloading is something of a last resort.)

And as for the possibility of carrying this thinking through to the rest of the language and standard library, I think that's a non-starter. The design of how Scala treats boxed Java types was settled at least a decade ago and the benefit would have to be very high indeed in order to justify the cost of pushing through such a fundamental change.

The only change in this vein I can envision perhaps being accepted would be adding an additional method, not named apply, that is overloaded as you propose. Option.unbox, I guess, by analogy with existing methods like Long.unbox (which translate nulls to 0). I dunno, though, Option\(x\).map(_.longValue) or Option\(x\).map(Long.unbox) seem not so bad to me. And I have not seen a need for this come up very often on SO, IRC, conversations with colleagues, etc.

@scabug
Copy link
Author

scabug commented Jan 30, 2016

@som-snytt said:
This is for fans:

scala> implicit class xxx(val o: Option.type) extends AnyVal {
     | def longmire(v: java.lang.Long): Option[Long] =
     |   o(v) map (Long.unbox) }

@scabug
Copy link
Author

scabug commented Feb 10, 2016

@lrytz said:
I agree with Seth, it looks to me like too much of a special case to be added to scala.Option. After all you need to provide the type parameter Option[Long] explicitly to trigger the NPE. This is consistent with existing behavior:

scala> (null: java.lang.Long): Long
java.lang.NullPointerException
  at scala.Predef$.Long2long(Predef.scala:368)
  ... 28 elided

@scabug scabug closed this as completed Sep 29, 2016
@scabug scabug added the quickfix label Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant