6

I have some Scala code that needs to call a Java API

The Java API takes arguments that may be null. My Scala, of course, uses Option.

For example, let's say I have a Java object constructor Foo(Integer) where the Integer may be null. I want to call it given a Scala bar: Option[Int].

I tried this

import scala.collection.JavaConversions._
import scala.collection.JavaConverters._

val foo = Foo( bar.getOrElse(null) )

But got this compile error

Error:(335, 44) type mismatch;
  found   : Any
  required: Integer
  bar.getOrElse(null),

What is the correct idiom for doing this?

rtruszk
  • 3,902
  • 13
  • 36
  • 53
opus111
  • 2,744
  • 4
  • 25
  • 41

3 Answers3

5

You don't need a Java method to reproduce this problem:

scala> import scala.collection.JavaConversions._
import scala.collection.JavaConversions._

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> class Foo(a: java.lang.Integer)
defined class Foo

scala> val bar: Option[Int] = Some(5)
bar: Option[Int] = Some(5)

scala> new Foo(bar.getOrElse(null))
<console>:16: error: type mismatch;
 found   : Any
 required: Integer
              new Foo(bar.getOrElse(null))
                                   ^

The problem is that Int can't be null, so the type of bar.getOrElse(null) is Any.

scala> bar.getOrElse(null)
res0: Any = 5

scala> bar.orNull
<console>:15: error: Cannot prove that Null <:< Int.
              bar.orNull
                  ^

So you've got to convert the Option's type parameter to something that can be null before you unwrap it in a nullable way.

Quickest way I can think of immediately:

scala> new Foo(bar.map(x => x: java.lang.Integer).orNull)
res18: Foo = Foo@cdc45e

Edit: Here, I thought of a more general way to do it!

implicit class RichOption[A](o: Option[A]) {
    def toRef[B >: Null](implicit f: A => B): B = o.map(f).orNull
}

Now you can write new Foo(bar.toRef) :)

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • Thank you Chris. I understand now. I wonder if some implicit could/should be added JavaConverters to make calling Java easier, and avoid the temptation to use NULL in your Scala code ;-) – opus111 Dec 22 '14 at 22:27
  • Thumbs up, but I would call it `toRef`. `x.toRef[Integer]`. The SO nanny doesn't allow increment operator in comments. It's weird being back in Kindergarten, the chairs are so small. – som-snytt Dec 23 '14 at 00:07
3

No sooner do I post the question, than I spot the answer in the related list (sorry)

Here's a solution

val foo = Foo(bar.getOrElse(null).asInstanceOf[java.lang.Integer])

Kind of clunky. Anyone have anything better?

opus111
  • 2,744
  • 4
  • 25
  • 41
  • 4
    The complaint I have this this solution is that `asInstanceOf` masks type errors. If `bar` is an `Option[String]`, this compiles but throws `ClassCastException`. – Chris Martin Dec 22 '14 at 22:25
3

More chatter:

scala> import runtime.BoxesRunTime.boxToInteger
import runtime.BoxesRunTime.boxToInteger

scala> val x = Some(42)
x: Some[Int] = Some(42)

scala> val y: Option[Int] = None
y: Option[Int] = None

scala> x.fold(null: Integer)(boxToInteger)
res0: Integer = 42

scala> y.fold(null: Integer)(boxToInteger)
res1: Integer = null

Of course it's better to

scala> x.fold(null: Integer)(i => i: Integer)
res2: Integer = 42

and even better

scala> x.fold[Integer](null)(identity)
res3: Integer = 42

scala> y.fold[Integer](null)(identity)
res4: Integer = null
som-snytt
  • 39,429
  • 2
  • 47
  • 129