scala 为什么在创建序列时会获得列表

w80xi6nr  于 2022-12-13  发布在  Scala
关注(0)|答案(3)|浏览(90)

为什么在使用以下代码创建Seq时会得到List?

scala> val s = Seq[Int]()
    s: Seq[Int] = List()

事实上,Seq是一个特征,而特征不能被初始化。

ct2axkht

ct2axkht1#

Seq是一个特征,List是一个(默认)实现。

object Seq extends SeqFactory.Delegate[Seq](List)
//                                          ^^^^

https://github.com/scala/scala/blob/v2.13.10/src/library/scala/collection/immutable/Seq.scala#L39

implicitly[List[Int] <:< Seq[Int]] // compiles, i.e. List[Int] is a subtype of Seq[Int]

Seq是一个特性,无法初始化特性
首先,即使X是一个trait,您也可以示例化一个扩展trait的匿名类:new X {}。(顺便说一下,List也是一个抽象类。)
其次,Seq[Int]()被去糖化为Seq.apply[Int](),你引用的不是trait Seq,而是它的伴随对象。

ryoqjall

ryoqjall2#

您从Seq伴随对象调用了applyapply的实现返回了List[Int],它是Seq[Int]的示例

5vf7fwbs

5vf7fwbs3#

没有魔法。
这里有一些简单的代码,大致如下所示:

object Seq:
  def apply[A](elements: A*) = List(elements: _*)

换句话说,Seq.apply会委派给List.apply,因此当您呼叫Seq(1, 2, 3)时,它会委派给List(1, 2, 3),而List(1, 2, 3)会传回List
Here is an extremely simplified runnable Scastie demonstration

enum MyList[+A]:
  case MyNil
  case MyCons(head: A, tail: MyList[A])

object MySeq:
  def apply[A](elements: A*) =
    elements.foldRight(MyList.MyNil: MyList[A]) { (x, xs) =>
      MyList.MyCons(x, xs)
    }

MySeq()        //=> MyNil: MyList[Nothing]

MySeq(1, 2, 3) //=> MyCons(1, MyCons(2, MyCons(3, MyNil))): MyList[Int]

真实的的代码当然要比这个简单的例子更一般和抽象,Seq对象本身没有apply方法,而是继承自SeqFactory.Delegate[Seq](List)

object Seq extends SeqFactory.Delegate[Seq](List) {
//         ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  override def from[E](it: IterableOnce[E]): Seq[E] = ???
}

SeqFactory.Delegate[Seq](List)apply方法则会委派给对象的apply方法,该对象是以delegate参数传递的:

object SeqFactory {
  class Delegate[CC[A]](delegate: SeqFactory[CC]) extends SeqFactory[CC] {
  //                    ↑↑↑↑↑↑↑↑
    override def apply[A](elems: A*): CC[A] = delegate.apply(elems: _*)
  //                                          ↑↑↑↑↑↑↑↑
  }

如果你还记得的话,delegate参数是List object

object Seq extends SeqFactory.Delegate[Seq](List) {
//                                          ↑↑↑↑
  override def from[E](it: IterableOnce[E]): Seq[E] = ???
}

List没有自己的apply方法,而是从IterableFactory继承,如下所示:

def apply[A](elems: A*): CC[A] = from(elems)

因此,它只是委托给from,这是一个抽象方法,它是这样实现的:

def from[B](coll: collection.IterableOnce[B]): List[B] = Nil.prependedAll(coll)

因此,from会委派给Nil.prependedAllNil会从List继承prependedAll,其外观如下所示:

override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): List[B] = prefix match {
  case xs: List[B] => xs ::: this
  case _ if prefix.knownSize == 0 => this
  case b: ListBuffer[B] if this.isEmpty => b.toList
  case _ =>
    val iter = prefix.iterator
    if (iter.hasNext) {
      val result = new ::[B](iter.next(), this)
      var curr = result
      while (iter.hasNext) {
        val temp = new ::[B](iter.next(), this)
        curr.next = temp
        curr = temp
      }
      releaseFence()
      result
    } else {
      this
    }
}

在本例中,您要构造一个空的Seq,您将遇到第二种情况:

case _ if prefix.knownSize == 0 => this

它只返回this,即Nil,它是List[A]的一个示例。因此,Seq[Int]()的 * 运行时类 * 是List[Int],但当然,* 静态类型 * 仍然是Seq[Int]
最常见的情况是第四种情况,它获取prefixiterator,然后在while循环中循环iterator。注意:这 * 不是 *“Scala方式”!Scala标准库为了性能和/或平台互操作性而采取了一些捷径,和/或仅仅是因为在库中的这个特定点,我们所习惯的高级抽象不可用,因为首先提供这些抽象的是标准库。
Scala的方法是fold,就像我上面做的那样。
因此,尽管真实的的Scala库要经过六个额外的间接层,但它与我上面编写的简化代码片段非常等效。

相关问题