4

我在一个 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数组,但它为(假设它没有正确初始化)

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

所以这会引发NullPointerException

我发现为了在 for comprehension 中使用它,我可以将它包装到一个 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

提前致谢!

4

3 回答 3

4

编辑:

Amap和 aflatMap总是必须返回相同的类型,它们被调用。如果你有一个列表,你总是会从map. 对于一个选项也是如此。如果您尝试在 flatMap 中混合 2 种类型,这很可能不起作用。什么应该

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

返回?Some(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]。它们都满足单子定律。最后,如果您愿意,您可以安全地调用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,因为不会为 执行映射NonegetOrElse然后,如果您需要解开该值,您可以简单地执行结果。

于 2012-04-12T21:31:32.240 回答
3

我不认为你的版本有getOrElse那么糟糕(你可以通过删除[Array[String]]after来缩短它Option,因为可以推断出来)。如果您想要更简洁的内容,请执行以下操作:

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

Option您还可以使用以下事实foreach

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

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

或者你可以拉皮条 MyClass

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

for (str <- myInstance.getSomeStringsOrNot) ...
于 2012-04-12T21:41:37.737 回答
2

简单的规则:有 a 的地方null,把它变成Option. 所以:

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

只有这样是行不通的。因为array,它会返回多个元素,但是因为第一个生成器是 an Option,所以它会返回一个Option。这两个元素是不利的:你不能返回Option多个元素中的一个。

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

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

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

于 2012-04-12T22:51:08.017 回答