我学习Scala已有一段时间了,无法清楚地了解Option的用法。当我链接函数时,它可以帮助我避免空值检查(根据docs)。这对我来说很清楚:))
接下来我看到Option可以作为开发人员的一种指示器,在这里可以使用null值并且必须处理它。这是真的吗?如果是,我应该使用选项吗?例如
class Racer {
val car = new Car()
}
我有Racer类,我确定car field不能为null(因为它是常量并在创建Racer实例时得到一个值)。这里不需要选项。
class Racer {
var car = new Car()
}
我在这里制作汽车可以改变。并且有人可以为汽车分配零。我应该在这里使用Option吗?如果是,我注意到我所有的班级字段都是Option的候选者。我的代码看起来像这样
class Racer {
var car: Option[Car] = None
var currentRace: Option[Race] = None
var team: Option[Team] = None
...
}
看起来不错吗?对我来说,似乎有点过度使用。
def foo(): Result = {
if( something )
new Result()
else
null
}
我有一个可以返回null的方法。我应该返回Option吗?如果方法可以返回null,我是否应该总是这样做?
任何有关它的想法都会有所帮助。提前谢谢!
我的问题与Why option类似,但我认为不一样。它更多地是关于的时候,而不是为什么。:)
答案 0 :(得分:37)
您应该尽可能避免在Scala中null
。它实际上只存在与Java的互操作性。因此,只要有可能函数或方法可以返回“无值”,或者逻辑上有效的成员变量具有“无值”,就可以使用null
而不是Option
。
关于您的Racer
示例:如果Racer
没有car
,currentRace
和team
,null
是否真的有效?如果没有,那么你不应该选择那些成员变量。不要只选择它们,因为理论上可以将它们分配给Racer
;只有在逻辑上你认为它是一个有效的null
对象时才会这样做,如果这些成员变量中的任何一个没有值。
换句话说,最好假装null
不存在。在Scala代码中使用def foo(): Option[Result] = if (something) Some(new Result()) else None
是代码气味。
Option
请注意,val opt = foo()
// You can use pattern matching
opt match {
case Some(result) => println("The result is: " + result)
case None => println("There was no result!")
}
// Or use for example foreach
opt foreach { result => println("The result is: " + result) }
有许多有用的方法可供使用。
var
另外,如果你想用函数式编程,你应该尽可能地避免使用可变数据,这意味着:避免使用val
,而是使用{{1}},使用不可变集合和尽可能使自己的类不可变。
答案 1 :(得分:10)
在Scala中进行函数式编程时,Option
比null
更可取,因为它是类型安全的,并且在函数范例中与其他结构一起使用时很好。
特别是,您可以使用Option
上的高阶函数轻松编写惯用代码。这个Scala Option Cheat Sheet对这个主题很有帮助。
答案 2 :(得分:8)
虽然Option
可以使您的类定义看起来冗长,但另一种方法是不知道何时需要测试是否定义了变量。我认为在你的例子中,如果你的类是不可变的(所有字段都是val
s),你就不需要这么多Option
。
对于方法foo
,如果返回null,则需要记录它,客户端需要阅读该文档。然后客户端会编写一堆代码,如:
val result = foo(x)
if (result != null) {
println(result)
}
如果您定义foo
以返回Option[Result]
,则类型系统会强制客户端处理未定义结果的可能性。他们也拥有收藏类的全部力量。
result foreach println
使用带有Option
的集合方法而不是测试null
的另一个好处是,如果您的方法扩展为多元素集合(例如List
) ,您可能根本不需要更改代码。例如,您最初可能认为您的Racer
只能有一个工作人员,因此您定义val crew: Option[Crew]
并且您可以使用crew.forall(_.age > 30).getOrElse(false)
测试工作人员是否超过30岁。现在,如果您将定义更改为val crew: List[Crew]
,您的旧代码仍会编译,现在您将检查所有机组成员是否超过30个。
有时您需要处理null
,因为您使用的库可能会返回它。例如,当您获得资源时,可能会得到null
结果。幸运的是,很容易防御性地包装结果,因此它们可以转化为一种选择。
val resource = Option(this.getClass.getResource("foo"))
如果getResource
返回null
,则resource
等于None
,否则为Some[URL]
。酷!
不幸的是,Option
可能会有一些开销,因为它涉及额外的对象创建。但是,与空指针异常相比,这种开销很小!
答案 3 :(得分:6)
Options
背后的想法是摆脱null
,所以是的,你应该使用它们而不是返回“null
或值”。这也是为什么,如果你想建模可选字段(0..1与其他对象的关系),使用Option
绝对是一件好事。
如你所提到的那样,它的一个小缺点就是它使得很多可选字段的类声明变得有点冗长。
还有一件事,在scala中,我们鼓励你使用“不可变对象”,所以在你的例子中,字段应该是val
个。 ;)