摘自《快学Scala》第十一章
一般还是用经典的模式:字母和数字的序列,以字母或下划线开头。
a 标识符 b
其中,标识符代表一个带有两个参数的方法(一个隐式的参数和一个显式的参数),如:
1 to 10
//实际上是
1.to(10)
这样的表达式称为中置表达式。如:
1 -> 10
//等同于
1.->(10)
在类中定义操作符:
//定义2个分数的乘积
// (n1 / d1) x (n2 / d2) = (n1n2 / d1d2)
class Fraction(n:Int,d:Int){
private int num = ...
private int den = ...
...
def *(other:Fraction) = new Fraction(num * other.num, den * other.den)
}
只有一个参数的操作符称为一元操作符。
后置操作符(postfix):
a 标识符 等同于 a.标识符()
1 toString 等同于 1.toString()
前置操作符(prefix):
+ - ! ~
它们被转换成对名为unary_
操作符的方法调用,例如:
-a 等同于 a.unary_-
在Scala中,所有的操作符都是左结合的,除了:
1 :: 2 :: Nil
等同于
1 :: (2 :: Nil)
我们首先要创建出包含2的列表,这个列表又被作为尾巴并到以1作为头部的列表中。
右结合的二元操作符是其第二个参数的方法,如:
2 :: Nil
等同于
Nil.::(2)
Scala允许你将如下的函数调用语法:
f(arg1, arg2, ...)
扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等同于调用:
f.apply(arg1, arg2,...)
如果它出现在赋值语句的等号左侧,表达式
f(arg1, arg2,...) = value
//等同于
f.update(arg1, arg2,...,value)
这个机制被用于数组和映射。例如:
val scores = new scala.collection.mutable.HashMap[String,Int]
scores("Bob") = 100 //调用scores.update("Bob",100)
val bobsScore= scores("Bob") //调用scores.apply("Bob")
apply方法同样被经常用在伴生对象中,用来构造对象而不是显式的使用new。假定我们有一个Fraction类:
class Fraction(n:Int, d:Int){ ... } object Fraction{ def apply(n:Int, d:Int) = new Fraction(n,d) } val result = Fraction(3,4) * Fraction(2,5)
所谓提取器,就是一个带有unapply方法的对象,可以把unapply方法当做是伴生对象中apply方法的反向操作。apply方法接受构造参数,然后将它们变成对象,而unapply方法接受一个对象,然后从中提取值——通常这些值就是当初用来构造该对象的值。
unapply方法返回的是一个Option,它包含一个元组,每个匹配到的变量各有一个值与之对应。在本例中,我们返回一个Option[(Int,Int)]
Object Fraction{
def unapply(input:Fraction) =
if (input.den == 0) None else Some((input.num, input.den))
}
你可以用提取器从任何类型的对象中提取信息。
举例来说,假定你想要从字符串中提取名字和姓氏:
val author = "Cay Horstmann"
val Name(first, last) = author //调用Name.unapply(author)
提供一个对象Name,其unapply方法返回一个Option[(String,string)]
.如果匹配成功,返回名字和姓氏的对偶。该对偶的两个组成部分将会分别绑到模式中的两个变量。如果匹配失败,则返回None.
object Name{
def unapply(input:String) = {
val pos = input.indexOf(" ")
if(pos == -1) None
else Some((input.substring(0,pos), input.substring(pos+1)))
}
}
每一个样例类都自动具备apply和unapply方法:
case class Currency(value:Double, unit:String)
//可以构造这样一个Currency实例
Currency(29.95, "EUR")
case Currency(amount, "USD") => println("$" + amount)
在Scala中,并没有只带一个组件的元组。如果unapply方法要提取单值,则它应该返回一个目标类型的Option,例如:
object Number{
def unapply(input:String):Option[Int] =
try{
Some(Integer.parseInt(input.trim))
}catch{
case ex:NumberFormException => None
}
}
//用这个提取器,你可以从字符串中提取数字
val Number(n) = "1729"
//提取器也可以只是测试输入而并不真的将值提取出来,这样的话,unapply方法返回Boolean。如:
object IsCompound{
def unapply(input:String) = input.contains(" ")
}
//可以用这个提取器给模式增加一个测试,如:
author match{
case Name(first, last @ IsCompound()) => ...
//如果作者是 Peter van der Linden 也能成功匹配
case Name(first, last) => ...
}
要提取任意长度得到值的序列,我们应该用unapplySeq来命名我们的方法它返回一个Option[Seq[A]],其中A是被提取的值的类型。如,Name提取器可以产出名字中所有组成部分的序列:
object Name{
def unapplySeq(input:String):Option[Seq[String]] =
if(input.trim == "") None else Some(input.trim.split("\\s+"))
}
这样,就能匹配并取到任意数量的变量了:
author match{
case Name(first, last) => ...
case Name(first, middle, last) => ...
case Name(first, "van","der",last) => ...
...
}