计算表达式时究竟发生了什么:Seq(1,2,3)
我是Scala的新手,现在对各种集合类型感到有点困惑。Seq
是一种特质,对吗?所以,当你这样称呼它:Seq(1,2,3)
,它一定是某种伴生对象?还是不想?是某种扩展Seq
的类吗?最重要的是,返回值的类型是什么?它是Seq
吗?如果是,为什么不显式地使用扩展类呢?
在REPL中,我还看到计算表达式的内容实际上是一个List(1,2,3)
,但类型显然是Seq[Int]
。为什么它不是IndexedSeq
集合类型,比如Vector
?这一切背后的逻辑是什么?
发布于 2019-05-15 12:33:34
计算表达式时会发生什么情况:
Seq(1,2,3)
?
在Scala中,foo(bar)
是foo.apply(bar)
的语法糖,除非this
也有一个名为foo
的方法,在这种情况下,它是隐式this
接收器上的方法调用,也就是说,就像Java一样,它相当于this.foo(bar)
。
就像任何其他OO语言一样,方法调用的接收方决定如何处理该调用,因此在本例中,Seq
决定要做什么。
Seq是一种特质,对吗?
标准库中有两个Seq
:
trait Seq
,这是一个类型。object Seq
,它是一个值。所以,当你把它叫做
Seq(1,2,3)
时,它一定是某种伴生对象?还是不想?
是的,它必须是一个对象,因为您只能对对象调用方法。因此,当您看到一个方法调用时,它必须是一个对象。一直都是。因此,在这种情况下,Seq
不可能是Seq
特性,它必须是Seq
对象。
请注意,“它必须是某种伴侣对象”不是真的。从这段代码中唯一能看到的就是Seq
是一个对象。您无法从这段代码中知道它是否是一个伴生对象。为此,您必须查看源代码。在这种情况下,它实际上是一个伴生对象,但您不能从您展示的代码中得出结论。
是某种扩展了Seq的类吗?
不是的。它不可能是类,因为您只能对对象调用方法,而且类不是Scala中的对象。(这与Ruby或Smalltalk不同,其中类也是Class
类的对象和实例。)它一定是个物体。
最重要的是,返回值的类型是什么?
找出这个问题的最简单的方法就是简单地查看Seq.apply文档
def apply[A](elems: A*): Seq[A]
使用指定的元素创建集合。
A
:集合元素的类型elems
:创建的集合的元素因此,如您所见,Seq.apply
的返回类型是Seq
,或者更准确地说,是Seq[A]
,其中A
是一个类型变量,表示集合中元素的类型。
它是Seq吗?如果是,为什么不显式地使用扩展类呢?
因为没有扩展类。
另外,Scala中的标准设计模式是同伴对象的apply
方法返回同伴类或特征的实例。打破这个惯例会很奇怪也很令人惊讶。
在REPL中,我还看到计算表达式的内容实际上是一个列表(1,2,3),但类型显然是SeqInt。
静态类型是Seq[Int]
。你只需要知道这些。这就是你所能知道的。
现在,Seq
是一个trait
,不能实例化特征,所以运行时类型将是Seq
的某个子类。但!您不能也不应该关心它是什么特定的运行时类型。
为什么没有索引的集合类型,如向量?这一切背后的逻辑是什么?
您怎么知道下次调用它时它不会返回Vector
?这一点也不重要,因为静态类型是Seq
,因此只允许在它上调用Seq
方法,并且只允许您依赖Seq
的契约,即Seq
的后条件、不变量等。即使您知道返回的是Vector
,您也无法使用这些知识做任何事情。
因此,Seq.apply
返回它可能返回的最简单的东西,即List
。
发布于 2019-05-15 11:57:28
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中,我还看到计算表达式的内容实际上是一个列表(1,2,3),但类型显然是SeqInt。
根据上面提到的代码,Seq
的默认实现是List
。
为什么没有索引的集合类型,如向量?这一切背后的逻辑是什么?
因为永恒的设计。该列表是不变的,使其不可变,并且具有一个恒定的前置操作,但是O(n)附加了操作成本和O(n)访问n‘’th元素的成本。Vector
具有持续有效的访问实现,并通过id、prepend和append操作添加元素。
要更好地理解清单是如何在Scala中设计的,请参阅https://mauricio.github.io/2013/11/25/learning-scala-by-building-scala-lists.html
https://stackoverflow.com/questions/56147577
复制相似问题