I have this Java class in a Jar file included as a dependency of an Scala program (like the Axis jar):
class MyClass {
private String[] someStrings;
public String[] getSomeStrings() { return someStrings; }
}
In my Scala program I have a Java API that return an instance of MyClass instance of MyClass to my program in Scala:
val myInstance = JavaAPI.getInstanceOfMyClass()
Then I try to use the someStrings array in my Scala program but it's null (let say that it wasn't properly initialized)
for(str <- myInstance.getSomeStrings()) ...
So this throws a NullPointerException .
I've found that in order to use it in the for comprehension I can wrap it into an Option so that it handles the NPE properly.
for(str <- Option[Array[String]](myInstance.getSomeStrings).getOrElse(Array[String]())
But that doesn't look OK to me.
Is there a way to create like an implicit method that takes that value even if it's null and wrap it into the Option, like:
implicit def wrapNull(a: Null): Option[Nothing] = None
implicit def wrapArray(a: Array[String]): Option[Array[String]] = Some(a)
So that when I do:
for(str <- myInstance.getSomeStrings())
I don't get the NPE
Thanks in advance!
edit:
A map
as well as a flatMap
always have to return the same type, on which they are called. If you have a List, you will always get a List back from map
. The same is true for an Option. If you try to mix 2 types in a flatMap, this will most likely not work. What should
Some(Array(1,2)).flatMap { x =>
x.map { _ * 2 }
}
return? Some(2,4) is not possible. So you get a type error. For this reason you have to do a nested map { map }
instead of flatMap { map }
.
In your case it would work like this:
case class A(b: B)
case class B(c: String)
val result = for(as <- Option(Array(A(B("foo")), A(B("bar"))))) yield {
for(a <- as; b <- Option(a.b); c <- Option(b.c)) yield {
c
}
}
The first for
takes an Option[Array[A]]
and returns an Option[Array[String]]
. The nested for takes an Array[A]
and returns an Array[String]
. They both satisfy the monad laws. At the end you can safely call getOrElse
on result
to unwrap the value if you want to.
original:
You could just do
val result = Option(myInstance.getSomeStrings).map { x =>
x.map { y =>
// do stuff with strings
}
}
or
val result = for(x <- Option(myInstance.getSomeStrings)) yield {
x.map { y =>
// do stuff with strings
}
}
You don't need to write the types because of the type inference and you don't need getOrElse
, because the map will not be executed for None
. You can then simply do a getOrElse
on result, if you need to unwrap the value.
I don't think your version with getOrElse
is that bad (you can make it a little shorter by removing the [Array[String]]
after Option
, since that can be inferred). If you want something even more concise, the following works:
for (str <- Option(myInstance.getSomeStrings).flatten) ...
You could also use the fact that Option
has a foreach
:
for {
strings <- Option(myInstance.getSomeStrings)
str <- strings
} ...
Note that you can't use yield
here, for the reason that drexin highlights in a comment below.
Or you could pimp MyClass
:
implicit def withNullWrapper(c: MyClass) = new {
def getSomeStringsOrNot() = Option(c.getSomeStrings).getOrElse(Array[String]())
}
for (str <- myInstance.getSomeStringsOrNot) ...
Simple rule: where there's a null
, make it an Option
. So:
for {
array <- Option(myInstance.getSomeStrings)
element <- array
thingy <- Option(element.method)
} yield thingy
Only that won't work. Because of the array
, it will return multiple elements, but because the first generator is an Option
, it will return an Option
. These two elements are adverse: you can't return an Option
of multiple elements.
The simplest alternative to fix the problem is to convert the Option
into an iterator or a collection (according to your taste). Like this:
for {
array <- Option(myInstance.getSomeStrings).toSeq
element <- array
thingy <- Option(element.method)
} yield thingy
Note that the second Option
need not be touched: the one that caused the problem was the one as the first generator. Option
anywhere other than the first generator is not a problem.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.