通过List
伴生对象的apply
方法来创建实例: List("A","B")
过程发生了什么
首先,List
伴生对象的apply
方法接收的是一个可变参数列表,即数组:
override def apply[A](xs: A*): List[A] = xs.toList
而我们传入的Array("A","B")
数组会被隐式转换为 WrappedArray
的子类型,这是在LowPriorityImplicits
里定义的:
// Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef]
// is as good as another for all T <: AnyRef. Instead of creating 100,000,000
// unique ones by way of this implicit, let's share one.
implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): WrappedArray[T] = {
if (xs eq null) null
else if (xs.length == 0) WrappedArray.empty[T]
else new WrappedArray.ofRef[T](xs)
}
随后对这个WrappedArray
的子类型ofRef[String]
类型,调用 toList
方法
不过在进行toList
时用到了隐式参数CanBuildFrom,我们先看一下List
伴生对象中定义的,用于生成CanBuildFrom信息的隐式方法:
/** $genericCanBuildFromInfo */
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] =
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
现在来追踪toList
的执行过程,在父类TraversableOnce
的toList
方法里调用了to
方法,而这个to
方法里有声明一个隐式参数。
用隐式参数CanBuildFrom
构造了一个List
类型的容器,把数据填充进去,再返回result
里面的隐式参数:
implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]
先不用管里面难懂的类型参数,编译在寻找对应的隐式参数值时,通过上面的 to[List]
声明的目标类型是List
,所以从List
的伴生对象中去寻找,通过 canBuildFrom
隐式函数得到了需要的参数,它是把一个可复用的对象造型成我们需要的CBF类型:
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
ReusableCBF
的意思是可复用的CanBuildFrom
,它在 GenTraversableFactory
里定义:
通过这个CBF隐式参数帮我们构造了一个新的容器,然后把当前集合里的数据放进去,最后再调用新容器的result
来得到List
通过断点,发现 b.result
时进入了 ListBuffer.toList
的代码里,也就是说这个隐式参数构造出来的新容器类型是 ListBuffer 的子类型。
最终,它返回ListBuffer
类里的start
成员,这个start
是一个 ::
类型(List的子类)
Pingback引用通告: scala雾中风景(9): List(1,2,3) == Seq(1,2,3) ? | 在路上
图片显示不了
图床问题,图片已更新
请教您一个问题,您翻译的那本红书里面,用函数式实现了List数据结构,且全部能够用尾递归实现,我知道List的本质是一个链表,但是我想知道为什么Scala官方没有用这个方式,而是用了过程式的实现。谢谢