令人讨厌的是,Scala 的一些最显眼和最棒的功能在表面之下却有着如此多的复杂性。所以,考虑一下这个简单的行:
val (head :: tail): ::[Int] = 1 :: Nil
:: 出现的三个位置中的每一个都指向不同的::,以及 Scala 中的不同机制。让我们按顺序逐一介绍。
head :: tail
这里发生的是模式匹配,就像人们看到case 语句一样。模式匹配可以出现在val 赋值、<- 左侧的for 理解以及case 语句中。
那么,这种特定的模式匹配是如何发生的呢?好吧,只要模式的格式为 a b c,Scala 就会将其转换为 b(a, c),然后将其转换为对对象 b 的 unapply 或 unapplySeq 的调用。
所以,val (head :: tail) 中的:: 指的是对象::(通过case class 定义)。
: ::[Int]
这是一个类型声明,所以::[Int] 是一个类型。 :: 本身是一个类,也是一个类型构造函数(因为它在给定类型参数的情况下构造类型——::[Int] 是一种类型,::[String] 是另一种类型,等等)。它也是List的子类,它只有两个子类:::和Nil的单例类。
这个声明是多余的,一般来说,很少有人使用:: 作为类型或类。我在这里展示它主要是为了完整性。
1 :: Nil
这里,:: 是一个方法。它是List的一个方法,所以,既然Nil是一个List而1不是,它必须属于Nil(或者可以通过隐式转换得到)。
这里要注意的机制是,以: 结尾的方法,当用于中缀运算符表示法时,绑定到右侧而不是左侧。或者,换句话说,a :: b 等价于b.::(a)。
这种机制很少使用,我怀疑主要是为了让习惯 fp 的程序员更熟悉传统的 fp 列表算法。它在 Scala 标准库和其他一些地方使用。
例如,在 Scala 2.8 上,现在有 +:,它的用途与 :: 相同,但为所有 Seq 定义。它由 :+ 镜像,它附加元素,其 : 除了消除与 + 的歧义之外没有其他用途,+ 被重载以连接字符串。