通过List.apply方法构造List的背后逻辑

通过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的执行过程,在父类TraversableOncetoList方法里调用了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的子类)

通过List.apply方法构造List的背后逻辑》上有3条评论

  1. Pingback引用通告: scala雾中风景(9): List(1,2,3) == Seq(1,2,3) ? | 在路上

发表评论

电子邮件地址不会被公开。 必填项已用*标注