如何在Scala中处理空输入参数

12

我知道在Scala中,一个方法应该永远不返回null... 但是对于输入参数呢?考虑下面的代码片段...

object MyObject {

    def myMethod(p: String): Option[String] = {
        if (p == null) throw new IllegalArgumentException("p is null.")
        ...
    }
}

这是我检查 p 是否正确的方式?有什么建议吗?

4个回答

23

惯例是Scala代码不使用null(只有极少数的例外情况,当使用那些库函数时应立即修复)。

因此,Scala中的null表示出了问题(至少是PBCAK),因此您可以抛出异常。这不是常规操作;这是一些严重的错误。在捕获严重的错误时捕获异常。捕获IllegalArgumentException而不是NullPointerException不会添加任何额外信息。只需保留原始内容。

如果代码来自Java,则处理它的规范方法是将其包装在Option中,这将将null转换为None。然后,您可能甚至不需要抛出异常;只需返回None

def myMethod(p: String) = Option(p).map(_.toLowerCase)

如果在null时无法继续,则需要考虑使用信息丰富的异常。 Option(p).orElse(throw new IllegalArgumentException("Null!")) 是表达抛出异常情绪的一种简洁方式。
在Scala 2.10中,您还可以将内容包装在scala.util.Try(...)中,它会自动捕获并打包异常。 如果您想要封装异常而不是抛出异常,则应该选择这种方法。(并使用Try而不是Option。)
import scala.util.Try
def myMethod(p: String) = Try(p.toLowerCase)

最后,对于更一般的替代结果处理,请使用Either。 错误处理的约定是期望的输出为Right(whatever),而Left(whatever)表示出现了问题。

1
由于我正在编写一个API,我认为更合适的结构是Try。 - j3d
1
在将 null 值包装在 Option 中时要小心。如果您依赖类型推断,有时可能会遇到麻烦。例如,如果您执行 Option(null).map(_ + 1),则会出现异常。但是,执行 Option(null: String).map(_.toLowerCase) 是可以的。 - Melvic Ybanez

7

有多种方法,而你的方法只是其中之一。

您也可以使用以下方法:

require(p != null, "p is null.")

或者更为实用的方法是使用 "Option":
def myMethod(p: String): Option[String] = {

  // no exception
  for {
    pp <- Option(p)
  } yield p + " foo bar"
}

编辑:

或者如果你想在不抛出异常的情况下获取错误信息,可以使用 Either

def myMethod(p: String): Either[Exception, String] = {
  if(p == null) Left(new IllegalArgumentException("p is null."))
  else Right(p + "foo bar")
}

2
不过这引出了一个问题:为什么不直接编写一个接受Option[String]的方法呢? - Dominic Bou-Samra
这就是为什么Scala是如此混乱的语言。对我来说,方法签名中的Option表示参数是可选的。如果参数不是可选的,而您又不希望调用者传递null或空值(使用Java),该怎么办?有时我想知道为什么人们不坚持简单并使用Java / C ++。它可能很冗长,但易读,并且在今天已经足够先进,可以用于分布式编程。 - animageofmine

1
Scala 的惯用语实际上是“永远不要使用 null 值”。所以,除非你正在编写一个需要适应肮脏的 Java 用户的 API,否则我就不会担心它。否则,你将不得不为你编写的每个方法的每个参数插入这个样板检查。
你美丽的一行代码方法
def square(x: String): String = math.pow(x.toInt, 2).toInt.toString

会变成一些尴尬的东西

def square(x: String): String = 
    if (x == null) throw new NullPointerException()
    math.pow(x.toInt, 2).toInt.toString
}

或者

def square(x: String): String = Option(x) match {
    case Some(x) => math.pow(x.toInt, 2).toInt.toString
    case None => throw new NullPointerException()
}

太可怕了。


Double是原始类型,它不能为null - Rex Kerr
改成了一个新的字符串示例 :) - Chris Martin

0

我最喜欢的方法是这样的:

object HandlingInputValidation {

  def main(args: Array[String]) = {
    println("A1: " + handleInput("Correct ") + " END")
    println("A2: " + handleInput("Correct") + " END")
    println("A3: " + handleInput("") + " END")
    println("A4: " + handleInput(" ") + " END")
    println("A5: " + handleInput(null) + " END")
  }

  def handleInput(data: String): Option[String] = {
    Option(data).map(_.trim.toLowerCase)
  }
 }

在这种情况下,null 将会被翻译成 None,并且空格将被删除。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接