我发现与Option[T]
作为集合的主要好处是您可以使用集合上定义的操作,例如map
, flatmap
, filter
, foreach
等等。这使得对给定选项进行操作变得更容易,而不是使用模式匹配或检查Option[T].isDefined
查看某个值是否存在。
例如,让我们以用户存储库为例丹尼尔·韦斯特海德 (Daniel Westheide) 博客文章介绍Option[T] http://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html:
假设你有一个UserRepository
根据 ID 返回用户的对象。用户可能存在也可能不存在,因此它返回一个Option[Person]
。现在假设我们要通过 id 搜索一个人,然后过滤他们的年龄。我们可以做的:
val age: Some[Int] = UserRepository.findById(1).map(_.age)
现在我们假设一个Person
还有一个gender
类型的属性Option[String]
。如果你想提取它,你可以使用map
:
val gender: Option[Option[String]] = UserRepository.findById(1).map(_.gender)
但使用嵌套选项不太方便。为此,你有flatMap
:
val gender: Option[String] = UserRepository.findById(1).flatMap(_.gender)
如果我们想打印出性别(如果存在),我们可以使用foreach
:
gender.foreach(println)
你会发现自己正在使用嵌套的 scala 类型Option[T]
定义字段,并且拥有类似集合的方法非常方便,可以帮助您消除样板文件和噪音,以从操作中提取实际值。
前几天我刚刚遇到的一个更现实的用例是使用 awscala SDK,我想从 S3 存储中检索一个对象:
val bucket: Option[Bucket] = s3.bucket(amazonConfig.bucketName)
val result: Option[S3Object] = bucket.flatMap(_.get(amazonConfig.offsetKey))
result.flatMap(s3Object =>
Source.fromInputStream(s3Object.content).mkString.decodeOption[Array[KafkaOffset]])
因此,这里发生的情况是,您向 S3 服务查询某个存储桶,该存储桶可能存在,也可能不存在。然后,你想提取一个S3Object
其中实际上包含数据,但 API 本身返回一个Option[S3Object]
,所以使用起来很方便flatMap
平坦地得到一个Option[S3Object]
代替Option[Option[S3Object]]
。最后,我想反序列化S3Object
它实际上包含一个 JSON,并使用 Argonaut 库,它返回一个Option[MyObject]
,然后再次使用flatMap
来拯救提取内部选项类型。
Edit:
正如你所指出的,map
and flatMap
属于一元属性Option[T]
。我写了一个博客文章 http://asyncified.io/2016/04/16/reducing-two-options-in-scala/描述了两个选项的减少,最终解决方案是:
def reduce[T](a: Option[T], b: Option[T], f: (T, T) => T): Option[T] = {
(a ++ b).reduceLeftOption(f)
}
这利用了++ http://www.scala-lang.org/api/current/index.html#scala.Option@++%5BB%5D(that:scala.collection.GenTraversableOnce%5BB%5D):Option%5BB%5D定义在任何集合上的运算符,该集合也专门定义在Option[T]
,作为一个集合。