我有来自 this question 的以下代码:
def addChild(n: Node, newChild: Node) = n match {
case Elem(prefix, label, attribs, scope, child @ _*) => Elem(prefix, label, attribs, scope, child ++ newChild : _*)
case _ => error("Can only add children to elements!")
}
里面的一切都很清楚,除了这个:child ++ newChild : _*
它有什么作用?
我知道有 Seq[Node]
与另一个 Node
连接,然后呢? : _*
做什么?
它“splats”1 序列。
查看构造函数签名
new Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding,
child: Node*)
这被称为
new Elem(prefix, label, attributes, scope,
child1, child2, ... childN)
但是这里只有一个序列,而不是 child1
、child2
等,因此这允许将结果序列用作构造函数的输入。
1 这在 SLS 中没有可爱的名字,但这里有详细信息。重要的是它改变了 Scala 将参数绑定到具有重复参数的方法的方式(如上面的 Node*
所示)。
_*
类型注解 包含在 SLS 的“4.6.2 重复参数”中。
参数部分的最后一个值参数可以用“*”作为后缀,例如 (..., x:T )。方法内部这种重复参数的类型就是序列类型 scala.Seq[T]。具有重复参数 T * 的方法采用可变数量的 T 类型参数。也就是说,如果将类型为 (p1 : T1, ... , pn : Tn,ps : S)U 的方法 m 应用于 k >= n 的参数 (e1, ... , ek),则采用 m在该应用程序中具有类型 (p1 : T1, . . . , pn : Tn,ps : S, . . . , ps0S)U,其中 k ¡ n 类型的 S 出现,其中 ps 之外的任何参数名称都是新的。此规则的唯一例外是如果最后一个参数通过 _ 类型注释标记为序列参数。如果将上面的 m 应用于参数 (e1, . . . , en,e0 : _),则该应用程序中 m 的类型被视为 (p1 : T1, . . . ., pn : Tn,ps :scala.序列[S])**
child ++ newChild - 序列
- 类型归属,帮助编译器理解的提示,该表达式有什么类型
_* - 接受任何值的占位符 + 可变参数运算符
child ++ newChild : _*
将 Seq[Node]
扩展为 Node*
(告诉编译器我们宁愿使用可变参数,而不是序列)。对于只能接受可变参数的方法特别有用。
a: _*
,您是在告诉编译器将 a
视为 _*
的一个实例,在本例中只是 Node*
以上所有答案看起来都很棒,但只需要一个示例来解释这一点。这里是 :
val x : Seq[Seq[Int]] = Seq(Seq(1),Seq(2))
def f(arg: Seq[Any]*) : Int = {
arg.length
}
f(x) //1 as x is taken as single arg
f(x:_*) // 2 as x is "unpacked" as a Seq[Any]*
所以现在我们知道 :_*
的作用是告诉编译器:请解压缩此参数并将这些元素绑定到函数调用中的 vararg 参数,而不是将 x 作为单个参数。
所以简而言之,:_*
是在将参数传递给 vararg 参数时消除歧义。
对于像我这样的一些懒人来说,它只是将 Seq 转换为 varArgs!