7

由于我阅读了有关Option帮助您避免 NullPointerException 的类的文章,因此我开始在各处使用它。想象一下这样的事情:

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 做吗,还是我走错了路?

4

5 回答 5

15

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

Optionmap当与和等函数式编程结构结合使用时,真正显示出它的威力foreach。这在处理多个选项时最有效。例如,假设我编写了一个方法,该方法接受一个字符串并返回一个文件,但前提是该文件存在并且是文件而不是目录:

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为我们做了所有的脏活:None 被映射到 None,并且Some(file)被映射到Some(fileinputstream). 简单的!

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

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

等等,这里发生了什么——所有的None? 难道我们不必注意并以某种方式处理它们吗?好吧,这就是它的flatMap用武之地:它将所有答案合并到一个列表中。 None是长度为零的答案,所以它忽略它。 Some(f)有一个答案f————所以它把它放在列表中。然后我们使用折叠将所有长度相加——现在列表中的所有元素都是有效的。挺好的!

于 2010-03-19T14:30:10.517 回答
12

最好不要解析的值Option,而是逻辑应用于它包含的任何内容:

findFile.foreach(process(_))

基本上,File如果找到一个,则处理 a,否则不执行任何操作(相当于 Thomas 的第一个for理解,因为for编译为对 的调用foreach)。这是一个更简洁的版本:

findFile match {
  case Some(f) => process(f)
  case None =>
}

更重要的是,这样做的好处是您可以链接操作,例如:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)
于 2010-03-19T12:30:30.690 回答
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 {
}

get 在 Scala 2.8.0.Beta-1 中没有被弃用。

于 2010-03-19T11:45:03.277 回答
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,我也想提取一个值。

于 2010-03-19T12:49:37.657 回答
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
}
于 2010-03-19T15:14:50.100 回答