在整个地方使用Option感觉有点尴尬。难道我做错了什么?

时间:2010-03-19 11:34:26

标签: scala scala-option

作为文章的结果,我阅读了有助于避免NullPointerException的Option类,我开始在整个地方使用它。想象一下这样的事情:

var file:Option[File] = None

以后我使用它时:

val actualFile = file.getOrElse(new File("nonexisting"))
if(actualFile.getName.equals("nonexisting")) { // instead of null checking

}
else { // value of file was good

}

做这样的事情对我来说感觉不那么“正确”。我还注意到.get已被弃用。 。这种东西是你们用Option做的,或者我走错了路?

5 个答案:

答案 0 :(得分:15)

返回Option然后使用getOrElse生成一些意味着“未找到”的哨兵值通常不是一个好主意。这就是Option的目的:表示找不到值!

Option在与mapforeach等函数式编程结构结合使用时,确实显示了它的强大功能。在处理多个选项时,这是最有效的。例如,假设我编写了一个接受字符串并返回文件的方法,但仅当文件存在且文件不是目录时才会显示:

import java.io._;
def niceFile1(s: String): File = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) f else null
}
def niceFile2(s: String): Option[File] = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) Some(f) else None
}

到目前为止,使用null更容易 - 至少在您忘记这可能会给您null并且您获得NPE之前。无论如何,我们现在尝试使用它。

def niceFopen1(s: String) = {
  val f = niceFile1(s);
  if (f!=null) new FileInputStream(f) else null;
}
def niceFopen2(s: String) = niceFile2(s).map(f => new FileInputStream(f))

看看发生了什么!在前一种情况下,我们必须手动进行逻辑测试并创建临时变量。啊!在第二种情况下,map为我们完成了所有脏工作:无映射到无,Some(file)映射到Some(fileinputstream)。简单!

但它变得更好了。也许我们想找到一大堆文件的大小:

def totalSize2(ss: Seq[String]) = {
  (0L /: ss.flatMap(niceFile2)){(sum,f) => sum+f.length}
}

等等,这里发生了什么 - 所有None怎么样?我们不是必须注意并以某种方式处理它们吗?好吧,那就是flatMap进来的地方:它将所有答案连接在一起。 None是零长度的答案,所以它忽略了它。 Some(f)有一个答案 - f - 所以它将它放在列表中。然后我们使用折叠来累加所有长度 - 现在列表中的所有元素都是有效的。太好了!

答案 1 :(得分:12)

不能解析 Option的值,但逻辑应用于其中包含的内容

,这是一个好主意:

findFile.foreach(process(_))

基本上,如果找到一个File,则处理for,否则不会执行任何操作(并且等同于Thomas'for理解,因为foreach编译为findFile match { case Some(f) => process(f) case None => } 的调用)。这是一个更简洁的版本:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)

更重要的是,关于这一点的好处是你可以操作,例如:

{{1}}

答案 2 :(得分:7)

在大多数情况下,您会使用模式匹配

file match {
   case Some(f) => { .. } //file is there
   case _ => { .. } //file is not there 
}

如果您只对该文件感兴趣,可以使用for表达式

for(f <- file) { //file is there 
}

然后,您可以链接表达式以在容器上的多个级别上工作

for{ 
  option <- List(Some(1), None, Some(2))
  f <- option
} yield f

res0: List[Int] = List(1, 2)

或者你可以使用isDefined并获取:

if(option.isDefined) {
   val x = option.get;
} else {
}
在Scala 2.8.0.Beta-1中不推荐使用

get。

答案 3 :(得分:2)

这是另一种选择:

var file:Option[File] = None
// ...
file map (new File(_)) foreach { fh =>
  // ...
}

但是,如果您需要在文件存在时执行某些操作,而如果不存在则需要执行其他操作,则match语句更合适:

var file:Option[File] = None
// ...
file map (new File(_)) match {
  case Some(fh) =>
    // ...
  case None =>
    // ...
}

这与if语句几乎相同,但我喜欢它更好地为Option这样的事物链接自然,我想在那里提取一个值。

答案 4 :(得分:1)

主要重述每个人都在说什么,但我认为当你有一个像这样可以为零的var时,你会遇到至少4种不同的情况:

1.文件不能为空

load(file.get())

当您尝试实际使用该文件时将抛出异常。快失败,耶!

2.该文件为空,但在这种情况下,我们有一个合理的默认值:

load(file.getOrElse(new File("/defaultFile")))

3.基于文件是否存在的两个逻辑分支:

  file match {
    case Some(f) => { .. } //file is there
    case _ => { .. } //file is not there 
  }

4.如果文件为空,则为无操作,即静默失败。在现实生活中,我不认为这个经常适合我:

for (f <- file) {
//do some stuff
}

或者,也许更清楚?

if (f.isDefined) {
  //do some stuff
}