如何将可以为null或数组的值隐式包装到Scala选项中

时间:2012-04-12 21:08:13

标签: scala null option implicit-conversion implicit

我在Jar文件中包含这个Java类作为Scala程序的依赖项(如Axis jar):

class MyClass {
    private String[] someStrings;
    public String[] getSomeStrings() { return someStrings; }
}

在我的Scala程序中,我有一个Java API,它将MyClass的MyClass实例实例返回给我在Scala中的程序:

val myInstance = JavaAPI.getInstanceOfMyClass()

然后我尝试在我的Scala程序中使用 someStrings 数组,但它是 null (假设它没有正确初始化)

for(str <- myInstance.getSomeStrings()) ...

因此抛出 NullPointerException

我发现为了在理解中使用它,我可以将它包装成一个Option,以便它正确处理NPE。

for(str <- Option[Array[String]](myInstance.getSomeStrings).getOrElse(Array[String]())

但这对我来说不太好看。

是否有一种方法可以创建类似于隐式方法,即使它为null并将其包装到Option中,也会获取该值,如:

implicit def wrapNull(a: Null): Option[Nothing] = None
implicit def wrapArray(a: Array[String]): Option[Array[String]] = Some(a)

所以当我这样做时:

for(str <- myInstance.getSomeStrings())

我没有得到 NPE

提前致谢!

3 个答案:

答案 0 :(得分:4)

编辑:

map以及flatMap始终必须返回相同的类型,并在其上调用它们。如果您有一个列表,您将始终从map返回一个列表。选项也是如此。如果您尝试在flatMap中混合使用2种类型,则很可能无法正常工作。应该是什么

Some(Array(1,2)).flatMap { x => 
  x.map { _ * 2 }
}

返回?有些(2,4)是不可能的。所以你得到一个类型错误。因此,您必须执行嵌套map { map }而不是flatMap { map }

在你的情况下,它会像这样工作:

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
  }
}

第一个for需要Option[Array[A]]并返回Option[Array[String]]。嵌套for需要Array[A]并返回Array[String]。他们都满足monad法则。最后,您可以安全地在getOrElse上致电result,以便根据需要解包值。

原:

你可以做到

val result = Option(myInstance.getSomeStrings).map { x =>
  x.map { y =>
    // do stuff with strings
  }
}

val result = for(x <- Option(myInstance.getSomeStrings)) yield {
  x.map { y =>
    // do stuff with strings
  }
}

由于类型推断而您不需要编写类型,因此不需要getOrElse,因为None不会执行地图。如果需要打开值,则可以在结果上执行getOrElse

答案 1 :(得分:3)

我认为您的getOrElse版本不是那么糟糕(您可以通过删除[Array[String]]之后的Option来缩短版本,因为可以推断出这一点。如果你想要更简洁的东西,以下工作:

for (str <- Option(myInstance.getSomeStrings).flatten) ...

您还可以使用Option具有foreach

的事实
for {
  strings <- Option(myInstance.getSomeStrings)
  str <- strings
} ...

请注意,您无法在此处使用yield,因为drexin会在下面的评论中突出显示。

或者你可以pimp MyClass

implicit def withNullWrapper(c: MyClass) = new {
  def getSomeStringsOrNot() = Option(c.getSomeStrings).getOrElse(Array[String]())
}

for (str <- myInstance.getSomeStringsOrNot) ...

答案 2 :(得分:2)

简单规则:如果有null,请将其设为Option。所以:

for {
  array <- Option(myInstance.getSomeStrings)
  element <- array
  thingy <- Option(element.method)
} yield thingy

只有这样才行不通。由于array,它会返回多个元素,但由于第一个生成器是Option,它将返回Option。这两个元素是不利的:您不能返回多个元素的Option

解决问题的最简单方法是将Option转换为迭代器或集合(根据您的喜好)。像这样:

for {
  array <- Option(myInstance.getSomeStrings).toSeq
  element <- array
  thingy <- Option(element.method)
} yield thingy

请注意,不需要触及第二个Option:导致问题的是第一个生成器。 Option除了第一个生成器之外的任何地方都不是问题。