scala中的中缀表达在3个地方存在:操作符、模式匹配、类型声明
1)中缀操作符最常见: a + b
或 x foo y
里的+
和foo
都是中缀操作符(严格的说scala里没有操作符,其实都是方法)。
其中需要注意的一点是名称最后以“:”结尾的,调用顺序与普通中缀操作符想反:从右往左。比如h :: t
实际是 t.::(h)
,它的意义在于可以让表达看起来更“顺眼”一些。比如我们自定义一个:
// 一个Cache单例,提供了 >>: 方法
scala> object Cache { def >>:(data:String) { println(data) } }
defined module Cache
scala> "myData" >>: Cache
myData
上面的方法很直观,让人一看就容易理解是把数据追加到cache中。还可以支持连续操作,稍修改一下:
scala> object Cache { def >>:(data:String):Cache.type = { println(data); Cache } }
defined module Cache
scala> "data2" >>: "data1" >>: Cache
data1
data2
res6: Cache.type = Cache$@45d2d12f
实际中,最常见的从右往左结合的中缀操作符是 List
的 ::
方法
scala> val list = List()
list: List[Nothing] = List()
scala> "A" :: "B" :: list
res4: List[String] = List(A, B)
2) 当一个高阶类型有2个参数化类型,比如
scala> class Foo[A,B]
在声明变量类型时,也可以用 A Foo B
中缀形式来表达,Foo
也称为中缀类型
scala> val x: Int Foo String = null
即 Int Foo String
等同于 Foo[Int,String]
3) 包含两个参数的构造器,在模式匹配时也可以用中缀表达
scala> case class Cons(a:String,b:String)
defined class Cons
scala> val x = Cons("one", "two")
x: Cons = Cons(one,two)
scala> x match { case "one" Cons "two" => println("ok") }
ok
即 "one" Cons "two"
等同于 Cons("one", "two")
对于2)和3)似乎也体现了某种一致性,不过暂对中缀类型的好处没有体会,对于类型系统中的这种风格也还不了解,暂不做比较。
现实中有个很容易迷惑人的地方,常用的 List
,即存在了一个::
方法, 也存在了一个::
名字的case类(List的子类)。
所以,构造一个list可以通过::
的伴生对象提供的工厂方法,也可以通过Nil的::
方法
scala> val l = ::("A",Nil) // 这里::是伴生对象, 相当于 ::.apply()
l: scala.collection.immutable.::[String] = List(A)
scala> val l2 = "A" :: Nil // 这里::是方法, 相当于 Nil.::()
l2: List[String] = List(A)
scala> l == l2
res1: Boolean = true
在模式匹配时,可以用中缀形式:
// 这里::是伴生对象,相当于 ::.unapply()
case head :: tail => …
所以::
在不同的场景,意思不同,有时是方法名,有时是伴生对象名。这个设计,最初了解时很爱吐槽。但它提供了一种看上去很一致的表象,在中缀形式下都是表示”头”与”尾”的连接。