::
is for List
s,事实上Seq.apply
目前将为您提供List
:
scala> val s = Seq(1,2,3)
s: Seq[Int] = List(1, 2, 3)
所以值的类型s
is Seq[Int]
,但它指向的对象是类型List[Int]
。没关系,因为List
延伸Seq
。这当然会匹配涉及的模式::
因为它实际上是一个List
:
scala> s match { case x :: xs => x }
res2: Int = 1
但表达的类型Seq(1,2,3)
is not List[Int]
but Seq[Int]
——即使实际的物体确实是一个List
。所以以下失败是因为Seq
没有定义一个::
method:
scala> val s = 1 :: Seq(2,3)
<console>:7: error: value :: is not a member of Seq[Int]
val s = 1 :: Seq(2,3)
你必须使用该方法Seq
反而:
scala> val s = 1 +: Seq(2,3)
s: Seq[Int] = List(1, 2, 3)
造成您困惑的关键是,当您对类似值调用方法时s
,可用的方法集完全取决于值的static类型,而模式匹配检查匹配的对象是否属于类::
.
为了展示这一点,让我们编译一些示例代码并使用javap
查看字节码;的前几条指令first
方法检查参数是否属于类::
(而不是其他一些扩展类Seq
) 并投射到它:
NS% cat Test.scala
object Test {
def first(xs: Seq[Int]) = xs match { case x :: xs => x }
}
NS% javap -c Test\$.class
Compiled from "Test.scala"
public final class Test$ {
public static final Test$ MODULE$;
public static {};
Code:
0: new #2 // class Test$
3: invokespecial #12 // Method "<init>":()V
6: return
public int first(scala.collection.Seq<java.lang.Object>);
Code:
0: aload_1
1: astore_2
2: aload_2
3: instanceof #16 // class scala/collection/immutable/$colon$colon
6: ifeq 30
9: aload_2
10: checkcast #16 // class scala/collection/immutable/$colon$colon
13: astore_3
14: aload_3
15: invokevirtual #20 // Method scala/collection/immutable/$colon$colon.head:()Ljava/lang/Object;
18: invokestatic #26 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
21: istore 4
23: iload 4
25: istore 5
27: iload 5
29: ireturn
30: new #28 // class scala/MatchError
33: dup
34: aload_2
35: invokespecial #31 // Method scala/MatchError."<init>":(Ljava/lang/Object;)V
38: athrow
最后,你可能会问为什么 Scala 人不做::
等效方法(前置一个元素)Seq
。如果他们有的话1 :: Seq(2,3)
会工作。
But for Seq
他们确实需要一个pair运算符,一个用于前置(该运算符必须以冒号结尾,以便它是右关联的),一个用于附加。您想避免将元素附加到List
因为你必须遍历现有的元素才能做到这一点,但对于Seq
一般而言——例如追加对于一个来说是相当有效的Vector
。所以他们选择了+:
用于前置和:+
用于追加。
当然,你可以问为什么他们不使用+:
for List
匹配Seq
。我不知道完整的答案。我确实知道::
来自具有列表结构的其他语言,因此部分答案可能是与既定约定的一致性。也许他们没有意识到他们需要一对匹配的运算符来作为超类型List
直到为时已晚——不确定。有谁知道这里的历史吗?