Scala中的Option

空值处理背景

在Java中,空值被定义为null。这是一个特殊的关键字,用来表示某个引用为空。正是因为这只是一个关键字,而不是一个对象,因此用它调用任何方法都是非法的。但是仔细思考,这一点实际上存在不合理的地方,我们明明希望返回的是一个对象,可是返回的却是一个关键字。

于是在Scala中,使用Option对象来对这一现象进行处理。Option类表示的是那种可能存在,也可能不存在的值,对应到Java中,就是说这个值可能为null。但是由于使用Option进行了一层包装,无论值是否存在,返回的都是一个对象,可以进行方法调用。一个很好的编程习惯是当方法的返回值有可能为null的时候,都使用Option对象来代替。

Option

为了使得一切皆对象的目标更加一致,Scala鼓励在变量和函数返回值可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None;在有值的时候,则使用Some来包装这个值。

Option[T]是一个带泛型的类,我们可以将其看作是一个包装了某个值的容器。SomeNone都是Option的子类。Some是一个class,表示有值;None是一个object,表示没有值。Some和None都可以调用isEmpty以及get方法:

  • 在调用isEmpty方法的时候,Some返回false,而None返回true
  • 在调用get方法的时候,Some返回对应被包装的value,而None会抛出NoSuchElementException错误

因此我们在接收一个Option对象之后,应该通过调用isEmpty来判断是否为空,然后选择如何处理。通过下面的例子,我们可以更好地理解Option的使用。

1
2
3
4
5
6
7
def toInt(in: String): Option[Int] = {
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: NumberFormatException => None
}
}

这里我们定义了一个将字符串转化为Int的函数,这个函数的返回值是有可能不存在的,因此我们将返回值定义为Option的形式。如果解析成功,能够返回,那么就使用Some进行包装,否则就返回None。

要使用这个函数,则需要在使用结果之前进行判断:

1
2
3
4
5
6
val maybeInt = toInt("123")
if(maybeInt.isEmpty){
println("not a Int")
}else{
println(maybeInt.get)
}

而借助于Scala中强大的模式匹配,我们也可以通过下面的方式来处理:

1
2
3
4
5
val maybeInt = toInt("xxx")
maybeInt match {
case Some(i) => println(i)
case None => println("not a Int")
}

简单总结Option的使用,就是将我们需要返回的实际值使用Option进行包装,其中泛型指定为对应的类型。当正常返回值的时候,使用Some进行包装,异常的时候则直接返回None。Option类型的值通常作为Scala集合类型操作的返回类型。

使用Option的好处在于返回了一个统一的对象,无论真实值是否存在,都能够进行方法的调用。在Java中,我们可以利用!= null来进行空值的处理,但是如果一个不小心没有处理到,就很容易出现NullPointException异常,并且出现异常之后,问题排查是一个比较难的问题。另一方面,一个Java方法是否会返回空值无法通过返回值看出来,只能通过看相关文档。而Scala的Option类型可以避免这种情况,使用Option类型就表示这个地方可能返回None。

Option类型本身还有一些相关的方法,列举如下:

方法 描述
def get: A 获取对应的值
def getOrElse(B): A 获取对应的值,如果是None则返回默认值
def isEmpty: Boolean 检查是否为None,是返回true,否则返回false
def isDefined: Boolean 与上面相反,检查是否为Some。是返回true,否则返回false

参考文章

  1. Scala中如何优雅的处理Null
  2. Scala Option | 菜鸟教程 (runoob.com)

Scala中的Option
http://example.com/2023/02/22/Scala中的Option/
作者
EverNorif
发布于
2023年2月22日
许可协议