使用Seq(1,2,3)创建Seq对象时会发生什么?

时间:2019-05-15 10:50:32

标签: scala traits

计算表达式Seq(1,2,3)时会发生什么?

我是Scala的新手,现在对各种集合类型有些困惑。 Seq是一个特征,对不对?因此,当您这样称呼Seq(1,2,3)时,它一定是某种伴随对象吗?或不?它是某种扩展Seq的类吗?最重要的是,返回值的类型是什么?是Seq,如果是,为什么不显式地使用扩展类呢?

也在REPL中,我看到评估表达式的内容实际上是一个List(1,2,3),但是类型显然是Seq [Int]。为什么没有索引集合类型(如Vector)?所有这些背后的逻辑是什么?

2 个答案:

答案 0 :(得分:7)

  

计算表达式Seq(1,2,3)时会发生什么?

在Scala中,foo(bar)foo.apply(bar)的语法糖,除非this也有一个名为foo的方法,在这种情况下,它是对隐式{ {1}}接收者,即就像Java一样,它等效于this

就像其他任何OO语言一样,方法调用 alone 的接收者决定如何处理该调用,因此,在这种情况下,this.foo(bar)决定如何处理。

  

Seq是一个特征,对吧?

标准库中有两个Seq

  

因此,当您这样称呼Seq时,它一定是某种伴随对象吗?还是不?

是的,它必须是一个对象,因为您只能在对象上调用方法。您不能在类型上调用方法,因此,当您看到方法调用时,它必须是一个对象。总是。因此,在这种情况下,Seq(1,2,3) 不可能Seq特质,它必须是Seq对象。

请注意,“它必须是某种伴侣对象”是不正确的。您从那段代码中唯一看到的是Seq是一个对象。您无法从那段代码中知道它是否是一个伴随对象。为此,您必须查看源代码。在这种情况下,事实证明它实际上是一个 伴侣对象,但是您不能从显示的代码中得出结论。

  

是某种扩展Seq的类吗?

不。 不可能是一个类,因为您只能在对象上调用方法,而类在Scala中不是对象。 (这与Ruby或Smalltalk不同,在Ruby中,类也是Seq类的对象和实例。)必须是对象。

  

最重要的是,返回值的类型是什么?

最简单的发现方法是看the documentation for Seq.apply

  

Class

     

使用指定的元素创建一个集合。

     
      
  • def apply[A](elems: A*): Seq[A] :集合元素的类型
  •   
  • A :创建的集合的元素
  •   
  • 返回具有元素元素的新集合
  •   

因此,如您所见,elems的返回类型为Seq.apply,或更准确地说,为Seq,其中Seq[A]是表示变量类型的类型变量。集合的元素。

  

是Seq吗,如果是,为什么不明确地使用扩展类呢?

因为没有扩展类。

此外,Scala中的标准设计模式是伴随对象的A方法返回伴随类或特征的实例。打破这一约定将是很奇怪且令人惊讶的。

  

在REPL中,我也看到评估表达式的内容实际上是一个List(1,2,3),但类型显然是Seq [Int]。

静态类型apply。那就是您需要知道的。这就是您所能知道的

现在,Seq[Int]Seq,并且无法实例化特征,因此运行时类型将是trait的某个子类。但!您不必也不必在乎它是什么特定的运行时类型。

  

为什么不像Vector这样的索引集合类型?所有这些背后的逻辑是什么?

您如何知道下次调用它不会返回Seq?没关系,因为静态类型是Vector,因此只允许您在其上调用Seq方法,并且只允许您依赖{{1} },即Seq的后置条件,不变式等。即使如果您知道它是返回的Seq,您也将无法使用此知识进行任何操作。

因此,Seq返回了它可能返回的最简单的东西,即Vector

答案 1 :(得分:4)

Seq是以下项中的val

package object scala {
...
  val Seq = scala.collection.Seq
...
}

它指向对象scala.collection.Seq

/** $factoryInfo
 *  The current default implementation of a $Coll is a `List`.
 *  @define coll sequence
 *  @define Coll `Seq`
 */
object Seq extends SeqFactory[Seq] {
  /** $genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

  def newBuilder[A]: Builder[A, Seq[A]] = immutable.Seq.newBuilder[A]
}

,当您执行Seq(1,2,3)时,apply()方法会从scala.collection.generic.GenericCompanion抽象类中剔除:

/** A template class for companion objects of "regular" collection classes
 *  represent an unconstrained higher-kinded type. Typically
 *  such classes inherit from trait `GenericTraversableTemplate`.
 *  @tparam  CC   The type constructor representing the collection class.
 *  @see [[scala.collection.generic.GenericTraversableTemplate]]
 *  @author Martin Odersky
 *  @since 2.8
 *  @define coll  collection
 *  @define Coll  `CC`
 */
abstract class GenericCompanion[+CC[X] <: GenTraversable[X]] {
...
  /** Creates a $coll with the specified elements.
   *  @tparam A      the type of the ${coll}'s elements
   *  @param elems  the elements of the created $coll
   *  @return a new $coll with elements `elems`
   */
  def apply[A](elems: A*): CC[A] = {
    if (elems.isEmpty) empty[A]
    else {
      val b = newBuilder[A]
      b ++= elems
      b.result()
    }
  }
}

最后,此方法通过上述代码构建Seq类型的对象

  

最重要的是,返回值的类型是什么?

object MainClass {

  def main(args: Array[String]): Unit = {

    val isList = Seq(1,2,3).isInstanceOf[List[Int]]

    println(isList)
  }
}

打印:

true

因此,类型为scala.collection.immutable.List

  

在REPL中,我也看到评估表达式的内容实际上是一个List(1,2,3),但类型显然是Seq [Int]。

根据上述代码,Seq的默认实现是List

  

为什么不像Vector这样的索引集合类型?所有这些背后的逻辑是什么?

由于设计不变。该列表是不可变的,并且使其不可变,并且具有恒定的前置操作,但是O(n)附加操作成本和访问第n个元素的O(n)成本。 Vector始终有效地实现访问,并通过id,prepend和append操作添加元素。

要更好地了解Scala中列表的设计方式,请参见https://mauricio.github.io/2013/11/25/learning-scala-by-building-scala-lists.html