Scala模式匹配的`@`操作符

阅读Spark源码的时候,经常碰到模式匹配中带着@这样的写法,如:

object CombineLimits extends Rule[LogicalPlan] {  
  def apply(plan: LogicalPlan): LogicalPlan = plan transform {
    case ll @ Limit(le, nl @ Limit(ne, grandChild)) =>
      Limit(If(LessThan(ne, le), ne, le), grandChild)
  }
}

大部分介绍Scala模式匹配的资料中都没有提到@的作用,今天特意研究了下他的用法:当需要处理匹配的对象本身时就可以用@将匹配对象绑定到一个变量上。

举例说明,我们定义个Person类:

scala> case class Person(name: String, age: Int)  
defined class Person  

然后实例化:

scala> val b = Person("Stan", 100)  
b: Person = Person(Stan,100)  

分别体会下以下几种用法: 首先是@的用法,p表示匹配的对象本身,当我们需要使用匹配中的对象时,就需要用@了。

scala> b match {  
     |   case p @ Person(_, age) => println(s"${p.name}, age: $age")
     |   case _ => println("Not a person")
     | }
Stan, age: 100  

还有一种用法,和上面的@用法很相似,不过是常见的类型匹配,这里的p也是一个匹配上的对象,和上面的区别是,这里p需要定义类型,而上面@的用法是个对象,对象中的age变量,我们是可以直接用的。

scala> b match {  
     |   case p: Person => println(p.name)
     |   case _ => println("Not a person")
     | }
Stan  

在看下这种用法,这种是不带@:的,匹配成功后,我们可以使用age变量,但是无法引用匹配的对象本身。

scala> b match {  
     |   case Person(_, age) => println(s"age: $age")
     |   case _ => println("Not a person")
     | }
age: 100  

这么对比来看就很容理解模式匹配中@的用法了。

附:关于不同类型的匹配

以下这种case执行会出错:

val a = 5

a match {  
  case p: Person => println(p.name)
  case _ => println("Not a person")
}

提示:

<console>:12: error: scrutinee is incompatible with pattern type;  
 found   : Person
 required: Int
              case p: Person => println(p.name)

这是应为编译器已经推断出a的类型为Int,已经确定无法匹配Person,就出错了。

如果要实现类型的效果怎么办?可以这么实现:

scala> def testMatch(ar: Any) {  
     |   ar match {
     |     case Person(_, age) => println(s"age: $age")
     |     case _ => println("Not a person")
     |   }
     | }
testMatch: (ar: Any)Unit

scala> testMatch(a)  
Not a person  

参考资料: