1

Java coder trying to use Scala here...

I have a generated Java builder that has a method

    public com.yada.SomeEntity.Builder setSomeValue(java.lang.Integer value) {
      ...
    }

From Scala, I can call this with an integer value or with null (and the field is deliberately defined to be nullable).

In my Scala code, I have, let's say

    val someInput: Option[Int] = someMethod()

The problem is that none of these work

  • builder.setSomeValue(someInput) - obviously, because it's an Option[Int] and not an Integer.
  • builder.setSomeValue(someInput.orNull) - because someInput.orNull is Any and not an Integer
  • builder.setSomeValue(someInput.orElseGet(null) - that's also an Any

I can do this

   builder.setSomeValue(if (someInput.isDefined) someInput.get else null)

Or alternatively, I can map something on someInput, but that breaks up the fluent style of the builder and just looks ugly.

Is there a pretty way to handle this in Scala?

2
  • 3
    Try with builder.setSomeValue(someInput.map(Int.box).orNull) - You have to manually box the value. - Another alternative would be builder.setSomeValue(someInnput.fold(ifEmpty = null)(Int.box)) Commented Jan 23 at 17:02
  • Nice! That does work! Commented Jan 23 at 17:43

2 Answers 2

2

TL;DR

A canonical solution is to use fold:

methodThatTakesNullableInteger(intOption.fold(null)(Int.box))

Why is it "canonical"?

All Scala collections that arise from algebraic data types have a canonical eliminator that is usually called fold-something:

  • Option[A] has fold[B](none: B)(some: A => B): B
  • List[A] has foldRight[B](nil: B)(cons: (A, B) => B): B
  • Either[X, Y] has fold[B](left: X => B, right: Y => B): B

Generally, these methods serve the same purpose: they take a value of type Coll[A] and eliminate it into a value of type B.

In each case, the signature of the method resembles 1:1 the structure of the collection. In a very specific sense, having such a fold method is equivalent to being that collection (keywords: initial F-algebras and all that racket).

In your case, you have Option[Int] (A = Int), and you want to eliminate it into B = java.lang.Integer. Therefore, you simply take your value of type Option[Int], and invoke fold with two arguments:

  • a n: java.lang.Integer to handle the case None (here: null)
  • a f: Int => java.lang.Integer to handle the case Some (here: Int.box)

and you obtain your java.lang.Integer, as desired.

Sign up to request clarification or add additional context in comments.

1 Comment

perhaps this is a perpetual debate, but I find .map(Int.box).orNull more readable 🤷
0

I have to admit I'm slightly surprised that there's no implicit conversion in this case. Since they do have issues, it's probably for the best. As suggested in a comment, someInput.map(Int.box).orNull or someInput.fold(ifEmpty = null)(Int.box). If you want this conversion to happen automatically, you can use an implicit conversion that does it for you:

def setSomeValue(value: java.lang.Integer): Unit =
  println(s"value is $value")

def someMethod(): Option[Int] =
  Some(1)

import scala.language.implicitConversions

given boxInt: Conversion[Int | Null, java.lang.Integer] with
  def apply(x: Int | Null): java.lang.Integer =
    x match {
      case n: Int => Int.box(n)
      case _ => null
    }

setSomeValue(someMethod().orNull)

You can play around with this code here on Scastie.

Please note that implicit conversions have drawbacks that might make it a poor fit for your use case, so pay attention (that's why you need to explicitly opt in). In this particular case, they might hide a boxing operation that you might not want to perform unless necessary due to performance reason. More in general, I found this answer to give a good explanation on the drawbacks of implicit conversions, although I would argue that, modulo performance considerations, this is probably a decently good use case. Finally, I can recommend to have a look at the chapter on implicit conversions from the Scala book.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.