可选值就是类型为Option[T]的一个值。其中,Option是标准库里的一个密封抽象类。T可以是任意的类型,例如标准类型或自定义的类。并且T是协变的,简单来说,就是如果类型T是类型U的超类,那么Option[T]也是Option[U]的超类。
Option类有一个子类:Some类。通过“Some(x)”可以构造一个Some的对象,其中参数x是一个具体的值。根据x的类型,可选值的类型会发生改变。例如,Some(10)的类型是Option[Int],Some(“10”)的类型是Option[String]。由于Some对象需要一个具体的参数值,所以这部分可选值用于表示“有值”。
Option类还有一个子对象:None。它的类型是Option[Nothing],是所有Option[T]类型的子类,代表“无值”。也就是说,Option类型代表要么是一个具体的值,要么无值。Some(x)常作为case语句的返回值,而None常作为通配模式的返回值。需要注意的是,Option[T]和T是两个完全没有关系的类型,赋值时不要混淆。
如果没有可选值语法,要表示“无值”可能会选用null,这就必须对变量进行判空操作。在Java里,判空是一个运行时的动作,如果忘记判空,编译时并不会报错,但是在运行时可能会抛出空指针异常,进而引发严重的错误。有了可选值之后,首先从字面上提醒读者这是一个可选值,存在无值和有值两种情况;其次,最重要的是,由于Option[T]类型与T类型不一样,赋值时就可能需要先做相应的类型转换。类型转换最常见的方式就是模式匹配,在这期间可以把无值None过滤掉。如果不进行类型转换,编译器就会抛出类型错误,这样在编译期就进行判空处理进而防止运行时出现更严重的问题。
可选值提供了一个方法isDefined,如果调用对象是None,则返回false,而Some对象都会返回true。还有一个方法get,用于把Some(x)中的x返回,如果调用对象是None则报错。
- 当我们访问一个map缺少的键/值对时,就会出现一个运行时错误:
val map = Map("a" -> 1)
val a = map("a")
println(a)
val b = map("b")
println(b)
1 java.util.NoSuchElementException: key not found: b scala.collection.immutable.Map M a p 1. a p p l y ( M a p . s c a l a : 114 ) a m m o n i t e . Map1.apply(Map.scala:114) ammonite. Map1.apply(Map.scala:114)ammonite.sess.cmd2 H e l p e r . < i n i t > ( c m d 2. s c : 4 ) a m m o n i t e . Helper.(cmd2.sc:4) ammonite. Helper.(cmd2.sc:4)ammonite.sess.cmd2 . < i n i t > ( c m d 2. s c : 7 ) a m m o n i t e . .(cmd2.sc:7) ammonite. .(cmd2.sc:7)ammonite.sess.cmd2$.(cmd2.sc:-1)
- 但是,
Map
通过get
方法提供了另一种访问键/值的方法。使用它将返回一个抽象类Option
类型的值。Option
有两个子类,Some 和 None
。
val map = Map("a" -> 1)
val a = map.get("a")
println(a)
val b = map.get("b")
println(b)
Some(1) None map: Map[String, Int] = Map(“a” -> 1) a: Option[Int] = Some(1) b: Option[Int] = None
Option
类也提供了get
方法,用来获取Some
包含的具体数值,但是不能用None
去调用,否则会报错;对于这种情况,我们可以使用getOrElse
方法,当 None
调用该方法时,可以返回指定的默认值。
val some = Some(1)
val none = None
println(some.get) // Returns 1
// println(none.get) // Errors!
println(some.getOrElse(2)) // Returns 1
println(none.getOrElse(2)) // Returns 2
1 1 2 some: Some[Int] = Some(1) none: None.type = None
个人觉得可选值的主要作用是:当你不确定某个数据或者函数的返回值到底有没有值时,你可以将该数据的类型定义成Option[T]
类型,这样无论它是有值(如Option[Int]
)还是没有值(Option[Nothing]
)都属于同一个类型,这样就不会出错。
当对象或函数有很多参数时,总是完全地指定它们可能是乏味和容易出错的。使用默认值的话有时参数并没有很好的默认值,那么这种情况下就可以使用Option[T]
类型的默认值None
。
下面的例子是将输入延迟一个时钟周期的模块。如果resetValue=None
(这是默认值),则寄存器将没有重置值,并被初始化为 garbage
。这可以避免使用正常范围以外的值来表示none
,例如使用-1
作为重置值来指示此寄存器未被重置。
class DelayBy1(resetValue: Option[UInt] = None) extends Module {
val io = IO(new Bundle {
val in = Input( UInt(16.W))
val out = Output(UInt(16.W))
})
val reg = if (resetValue.isDefined) { // resetValue = Some(number)
RegInit(resetValue.get)
} else { //resetValue = None
Reg(UInt())
}
reg := io.in
io.out := reg
}
三、chisel模式匹配使用实例
上述例子,也可以改成使用模式匹配来实现,如下所示:
class DelayBy1(resetValue: Option[UInt] = None) extends Module {
val io = IO(new Bundle {
val in = Input( UInt(16.W))
val out = Output(UInt(16.W))
})
val reg = resetValue match {
case Some(r) => RegInit(r)
case None => Reg(UInt())
}
reg := io.in
io.out := reg
}