何时使用Option

Sot*_*ric 26 scala

我学习Scala已有一段时间了,无法清楚地了解Option的用法.当我链接函数时,它可以帮助我避免空值检查(根据文档).这对我来说很清楚:)

接下来我看到Option可以作为开发人员的一种指示器,这里可以使用null值,并且必须对其进行处理.这是真的吗?如果是,我应该使用选项吗?例如

class Racer {
    val car = new Car()
}
Run Code Online (Sandbox Code Playgroud)

我有Racer类,我确定car field不能为null(因为它是常量并在创建Racer实例时得到一个值).这里不需要选项.

class Racer {
    var car = new Car()
}
Run Code Online (Sandbox Code Playgroud)

在这里,我这样做,以便汽车可以改变.并且有人可以为汽车分配零.我应该在这里使用Option吗?如果是,我注意到我所有的班级字段都是Option的候选者.我的代码看起来像这样

class Racer {
    var car: Option[Car] = None
    var currentRace: Option[Race] = None
    var team: Option[Team] = None
    ...
}
Run Code Online (Sandbox Code Playgroud)

它看起来不错吗?对我来说,似乎有点过度使用.

def foo(): Result = {
    if( something )
        new Result()
    else
        null
}
Run Code Online (Sandbox Code Playgroud)

我有一个可以返回null的方法.我应该返回Option吗?如果方法可以返回null,我是否应该总是这样做?

任何有关它的想法都会有所帮助.提前致谢!

我的问题类似于为什么选项,但我认为它不一样.它更多的是关于何时,而不是为什么.:)

Jes*_*per 40

您应该尽可能避免nullScala.它实际上只存在与Java的互操作性.因此,代替null,Option只要有可能函数或方法可以返回"无值",或者逻辑上有效的成员变量具有"无值".

关于你的Racer例子:Racer如果没有a car,是否真的有效,currentRace并且team?如果没有,那么你不应该选择那些成员变量.不要只是选择它们,因为理论上可以将它们分配给它们null; 只有在逻辑上你认为它是一个有效的Racer对象,如果这些成员变量中的任何一个没有值.

换句话说,最好假装好像null不存在.采用null在Scala代码是一个代码味道.

def foo(): Option[Result] = if (something) Some(new Result()) else None
Run Code Online (Sandbox Code Playgroud)

请注意,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) }
Run Code Online (Sandbox Code Playgroud)

此外,如果要以函数样式进行编程,则应尽可能避免使用可变数据,这意味着:避免使用var,val改为使用不可变集合,尽可能使自己的类不可变.

  • 除了"假装'如果`null`不存在"之外,如果你必须处理给你的`foo`可能是`null`的Java代码,你可以使用`Option(foo)`,将`null`变为'None`,将其他任何东西变成`Some(blah)`. (11认同)

pad*_*pad 10

当谈到Scala中的函数式编程时,它Optionnull类型安全更好,并且在函数范例中与其他构造相得益彰.

特别是,您可以使用高阶函数轻松编写惯用代码Option.此Scala选项备忘单是对该主题的有用阅读.


sch*_*mmd 8

虽然Option可以使您的类定义看起来冗长,但另一种方法是不知道何时需要测试是否定义了变量.我认为在你的例子中,如果你的类是不可变的(所有的字段都是vals),你就不需要这么多了Option.

对于您的方法foo,如果返回null,则需要记录它,客户端需要阅读该文档.然后客户端将编写一堆代码,如:

val result = foo(x)
if (result != null) {
  println(result)
}
Run Code Online (Sandbox Code Playgroud)

如果您定义foo为返回an Option[Result],则类型系统会强制客户端处理未定义结果的可能性.他们还拥有集合类的全部功能.

result foreach println
Run Code Online (Sandbox Code Playgroud)

使用collections方法Option而不是测试的另一个好处null是,如果您的方法扩展到多元素集合(例如a List),则可能根本不需要更改代码.例如,您最初可能假设您Racer只能拥有一名工作人员,因此您可以定义val crew: Option[Crew]并且可以测试工作人员是否超过30岁crew.forall(_.age > 30).getOrElse(false).现在,如果您将定义更改为val crew: List[Crew]旧代码仍然可以编译,现在您将检查所有机组成员是否超过30.

有时您需要处理,null因为您使用的库可能会返回它.例如,当您获得资源时,您可能会得到一个null结果.幸运的是,很容易防御性地包装结果,因此它们可以转化为一个选项.

val resource = Option(this.getClass.getResource("foo"))
Run Code Online (Sandbox Code Playgroud)

如果getResource返回nullresource等于None,否则它是a Some[URL].凉!

不幸的是,Option它可能会有一些开销,因为它涉及额外的对象创建.但是,与空指针异常相比,这种开销很小!


Nic*_*las 6

背后的想法Options是摆脱null,所以是的,你应该使用它们而不是返回" null或值".这也是为什么,如果你想建模可选字段(0..1与其他对象的关系),使用Option绝对是一件好事.

如你所提到的那样,它的缺点是它使得很多可选字段的类声明变得有点冗长.

还有一件事,在scala中,我们鼓励你使用"不可变对象",所以在你的例子中,字段应该是一些val.;)

  • 这个想法是在像新车这样的事件发生时建立一个新对象:`class Racer(name:String,car:Option [Car]){def receiveANewCar(c:Car)= new Racer(name,Some(c) ); def brokeHisCar = new Racer(name,None)}` (4认同)