它既是一个类又是一个方法。Cons 是一个类型参数化的类。List[A] 有两个子类:Cons 和 Nil。由于 Cons 是一个类,它可以由其构造函数创建,如下所示:
val s = new ::[Int](4, Nil)
Cons 是一个案例类,我们在进行模式匹配时使用构造函数。Cons 也是列表类上的一个方法,在其两个子类中实现。因此,我们可以在上面创建的 cons 类上使用 cons 方法。
val s1 = s.::(5)
可能会出现部分混淆,因为我们通常使用 List 对象的 apply 方法创建 List:
val s2 = List(1, 2, 3)
通常,对象的 apply 方法返回一个与对象同名的类的新实例。然而,这只是惯例。在这种特殊情况下,列表对象返回 Cons 子类的新实例。List 类本身是一个密封的抽象类,因此无法实例化。所以上面的 apply 方法做了以下事情:
val s2 = 1 :: 2 :: 3 :: Nil
任何以 ':' 结尾的方法都是其右侧操作数上的方法,因此可以将其重写为
val s2 = 1 :: (2 :: (3 :: Nil)) //or as
val s2 = Nil.::(3).::(2).::(1)
因此,Nil 对象上的 Cons(::) 方法将 3 作为参数并生成 Cons 类的匿名实例,其中 3 作为其头部,对 Nil 对象的引用作为其尾部。让我们将此匿名对象称为 c1。然后在 c1 上调用 Cons 方法,将 2 作为其参数返回一个新的匿名 Cons 实例化,我们将其称为 c2,其头部为 2,尾部为对 c1 的引用。最后,c2 对象上的 cons 方法将 1 作为参数,并返回命名对象 s2,其中 1 作为其头部,对 c2 的引用作为其尾部。
第二个混淆点是 REPL 和 Scala 工作表使用类的 toString 方法来显示结果。所以工作表给了我们:
val s3 = List(5, 6, 7) // s3 : List[Int] = List(5, 6, 7)
val s4 = List() // s4 : List[Nothing] = List()
val s5: List[Int] = List() // s5 : List[Int] = List()
s3.getClass.getName // res3: String = scala.collection.immutable.$colon$colon
s4.getClass.getName // res4: String = scala.collection.immutable.Nil$
s5.getClass.getName // res5: String = scala.collection.immutable.Nil$
如上所述 List 是密封的,因此无法创建新的子子类,因为 Nil 是一个对象,而 Cons 是最终的。由于 Nil 是一个对象,因此无法对其进行参数化。Nil 继承自 List[Nothing]。乍一看这听起来没什么用,但请记住这些数据结构是不可变的,所以我们永远不能直接添加它们,Nothing 是每个其他类的子类。所以我们可以毫无问题地将 Nil 类(间接地)添加到任何 List 中。Cons 类有两个成员,一个是 head,另一个是 List。当您计时时,它是一个相当简洁的解决方案。
我不确定这是否有任何实际用途,但您可以使用 Cons 作为类型:
var list: ::[Int] = List (1,2,3).asInstanceOf[::[Int]]
println("Initialised List")
val list1 = Nil
list = list1.asInstanceOf[::[Int]] //this will throw a java class cast exception at run time
println("Reset List")