到处使用 Option 感觉有点尴尬。我做错了什么吗?

发布于 2024-08-25 13:58:51 字数 448 浏览 11 评论 0原文

由于我读到了有关 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做这种事情吗,还是我走错了路?

As a result of articles I read about the Option class which helps you avoid NullPointerException's, I started to use it all over the place. Imagine something like this:

var file:Option[File] = None

and later when I use it:

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

}
else { // value of file was good

}

Doing stuff like this doesn't feel all that "right" to me. I also noticed that .get has become deprecated. . Is this sort of stuff what you guys are doing with Option's too, or am I going the wrong way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

丶视觉 2024-09-01 13:58:51

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

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

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 - 因此它将其放入列表中。然后我们使用折叠将所有长度相加——现在列表中的所有元素都是有效的。相当不错!

It's generally not a good idea to return Option and then use getOrElse to produce some sentinel value that means "not found". That's what Option is designed for: to signify that a value is not found!

Option really shows its power when used in conjunction with functional programming constructs like map and foreach. This is most potent when dealing with multiple options. For example, suppose I write a method that takes a string and gives me back a file, but only if the file exists and is a file not a directory:

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
}

So far, using null is easier--at least until you forget that this might give you null and you get a NPE. Anyway, let's now try to use it.

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))

Look what happened! In the former case, we had to do logic tests by hand and create temporary variables. Ugh! In the second case, map did all the dirty work for us: None was mapped to None, and Some(file) was mapped to Some(fileinputstream). Easy!

But it gets better yet. Maybe we want to find the size of a whole bunch of files:

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

Wait, what is going on here--what about all the None? Don't we have to pay attention and handle them somehow? Well, that's where flatMap comes in: it joins together all the answers into a single list. None is an answer of length zero, so it ignores it. Some(f) has one answer--f--so it puts it in the list. Then we use a fold to add up all the lengths--now that all the elements in the list are valid. Pretty nice!

乖不如嘢 2024-09-01 13:58:51

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

findFile.foreach(process(_))

基本上,这会处理一个>File 如果找到并且不执行任何操作(并且相当于 Thomas 的第一个 for 理解,因为 for 编译为对 foreach< 的调用/代码>)。它是更简洁的版本:

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

更重要的是,它的伟大之处在于您可以链接操作,例如:

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

It's a good idea to not resolve the value of the Option, but to apply logic to whatever is it contains:

findFile.foreach(process(_))

Basically this processes a File if one is found and does nothing otherwise (and is equivalent to Thomas' first for comprehension because for compiles to a call to foreach). It is a more concise version of:

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

What's more, the great thing about this is that you can chain operations, something like:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)
江城子 2024-09-01 13:58:51

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

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 和 get:

if(option.isDefined) {
   val x = option.get;
} else {
}

get is Scala 2.8.0.Beta-1 中未弃用。

In most cases you would use pattern matching

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

If you're only interested in the file if it is there you can use a for expression

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

You can then chain for expressions to work on multiple levels on containers

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

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

Alternatively you can use isDefined and get:

if(option.isDefined) {
   val x = option.get;
} else {
}

get is not deprecated in Scala 2.8.0.Beta-1.

耶耶耶 2024-09-01 13:58:51

这里有一个替代方案:

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 这样的东西,我也想提取一个值。

Here's an alternative:

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

However, if you need to do something if the file exists, and something else if it does not, a match statement is more appropriate:

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

That's pretty much the same as an if statement, but I like it's chaining nature better for things like Option, where I want to extract a value as well.

葬﹪忆之殇 2024-09-01 13:58:51

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

1. 文件不能为 null

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
}

Mostly restating what everyone is saying, but I think there's at least 4 different situations you will encounter when you have a nullable var like this:

1. The file must not be null

load(file.get())

Will throw an exception when you try to actually use the file. Fail fast, yay!

2. The file is null, but in this case we have a sensible default:

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

3. Two branches of logic based on whether the file exists:

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

4. No-op if the file is null, aka silently fail. I don't think this one is very often preferable for me in real life:

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

Or, maybe more clearly?

if (f.isDefined) {
  //do some stuff
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文