前面说过,在写模式匹配时,你必须保证你所写的可选项覆盖了全部的可能性,因此常常你必须加上一个缺省通配符选项。但这种情况只适应于缺省通配符有意义的情况。如果对于一些没有缺省项的情况,你怎么才能保证你写的可选项是完全的呢?
实际上,你可以借助于Scala编译器来帮忙,要做到这一点,编译器需要预先知道所有可能的匹配项,这种通常情况下是不可能的。比如你总可以派生出新的子类,然后再可选项中添加这个新创建的子类的模式。
一种可能的实现是为基类添加上Sealed关键字,一个sealed的类只能在定义它的同一个文件中定义它的子类。这样你就只需要关心已经定义的子类,如果你使用这些子类做为模式定义,如果可选项不去全的话,编译器会自动警告。
我们还是使用之前定义的表达式的例子:
sealed abstract class Expr case class Var(name:String) extends Expr case class Number(num:Double) extends Expr case class UnOp(operator:String, arg:Expr) extends Expr case class BinOp(operator:String,left:Expr,right:Expr) extends Expr
下面我们定义一个不完全的模式匹配:
def describe(e:Expr) :String =e match{ case Number(_) => "a number" case Var(_) => "a variable" } <console>:12: warning: match may not be exhaustive. It would fail on the following inputs: BinOp(_, _, _), UnOp(_, _) def describe(e:Expr) :String =e match{ ^ describe: (e: Expr)String
编译器给出警告,表示你的定义可能会抛出MatchError异常,因为BinOp和UnOp没有定义在模式定义中。
当有的时候,你可能只需要匹配部分模式,一是添加一个缺省匹配,比如通配符模式,例如:
def describe(e:Expr) :String =e match{ case Number(_) => "a number" case Var(_) => "a variable" case _ => throw new RuntimeException }
为简洁起见,Scala支持使用标注(annotation)的方法暂时取消编译器检查模式定义是否完备,为变量添加@unchecked标注后,编译器不再给出警告:
def describe(e:Expr) :String =(e: @unchecked) match{ case Number(_) => "a number" case Var(_) => "a variable" }
@unchecked在模式匹配中具有特殊意义,如果模式匹配的变量使用该标准,Scala编译器不对该模式进行完备性检查。