Hello, I have mixed java and scala code, but there was an unexpected result … I will give an example:
Java class:
public class A {
public String getValue(String v) {
if (v.equals("0")) return null;
else
return v;
}
}
public class B {
// Let's say that the function can return null
public A createA() {
return new A();
}
}
Scala ( scalaVersion := “2.13.11”) use class java:
object Main {
def main(args: Array[String]): Unit = {
val b = new B()
val r1: Option[String] = Option(b.createA()).map(a => a.getValue("return not null"))
val r2: Option[String] = Option(b.createA()).map(a => a.getValue("0")) // return Some(null)
println(r1.getOrElse("empty"))
println(r2.getOrElse("empty"))
// or matching
r2 match {
case Some(value) => println(value) // value == null
case None => println("empty")
}
}
}
output:
r1: return not null
r2: null
r2 match: null
Why the result of the function .map( Some(f(this.get)) ) immediately wraps in Some?
@inline final def map[B](f: A => B): Option[B] =
if (isEmpty) None else Some(f(this.get)) // function f() can return null
That’s one of common misconceptions about using Option.map
which does not automatically protect you against null by wrapping them into None
. What you should do instead is to use flatmap with another Option.apply
. eg.
Option(b.createA()).flatMap(a => Option(a.getValue("0"))) // return None`
2 Likes
Is there any reason for this implementation of the ‘map’ method?
I thought that the implementation in the ‘map’ method would be similar to the Java ‘Optional .map’.
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
The assumption is that you don’t have null
in proper Scala code. There is one specific function, Option#apply()
, specifically for guarding against null
values from Java interop, which must be applied to any potential null
value immediately at the Java/Scala boundary. (Perhaps it should rather be called #safeFromJava()
or similar, rather than #apply()
.)
The remainder of the Option
API is perfectly consistent and useful in proper Scala with the absence of null
values. Littering its implementation with guards against null
values would implicitly signal that these are expected and ok in Scala. They aren’t. It’d be a first step in the wrong direction - and down the rabbit hole, as it’s hard to argue why null
checks only should be applied in the Option
API implementation, once you start doing it at all.
2 Likes
Yes, you are right, I tried to write the implementation of classes A and B on the scala and the compiler immediately threw an error.
Technically, it should be possible to reimplement any Java class in Scala. Your example A
class in Scala would be
class A:
def getValue(v: String): String =
if (v.equals("0")) null else v
Scala does have null
. We don’t really want it, this is what we pay for piggy-backing on the JVM type system and seamless Java interop. But it is strongly discouraged, both in OO and functional style, to use null
in Scala code in any way, which includes allowing null
values to enter the system from Java interop.
2 Likes