要回答您的问题,我们需要看看 Scala 编译器必须执行重载解析时会发生什么。这在 SLS 6.23.3(对于 Scala 2.9)中有描述。
让我们看一个稍微简单一点的例子:
object Test {
def apply[T](x1: T) = "one arg" // A
def apply[T](x1: T, x2: T) = "two args" // B
def apply[T](elems: T*) = "var-args: " + elems.size // C
}
看看这三个调用:
Test(1) // fails, ambiguous reference, A and C both match arguments
Test[Int](1) // returns "one arg"
Test(1,2) // returns "two args", not "var-args: 2"
让我们从第一个开始。首先,编译器查看每个参数的形状,这是一种基本上描述参数是值还是函数的类型。在这里,没有难度,1
是一个很正常的、无聊的值,它的形状就是类型Nothing
。
现在它有一个1
类型的参数Nothing
,并找到适用于它的所有替代方案。它找到其中两个:
apply[T](x1: T)
:它接受一个无界类型的参数,所以它可以接收一个类型的参数Nothing
,
apply[T](elems: T*)
:它可以应用于任何数量(0
包括)相同无界类型的参数,因此它可以接收类型的单个元素Nothing
。
如果只有一个,它会停在那里并选择那个。
第二步与上述相同,只是这次它使用未定义的预期类型键入每个参数。基本上在这里,它查看剩下的两个替代方案,并确定它们是否适用于1
type的参数A <: Int
。不走运,他们俩都是。如果你是两个人改变apply[T](x1: T)
并apply(x1: String)
留下另一个人,那么这里将只剩下一个适用的替代方案,它会成功并停止。
然后编译器计算relative weight
彼此留下的每个备选方案的 。SLS 指出
备选方案 A 相对于备选方案 B的相对权重是一个从 0 到 2 的数字,定义为
- 如果 A 与 B 一样具体,则为 1,否则为 0,并且
- 如果 A 在从定义 B 的类或对象派生的类或对象中定义,则为 1,否则为 0。
在这一点上,必须有一个选项的分数高于所有其他选项,否则会出现歧义错误。我们可以忽略“定义”部分,它们是在同一个地方定义的。
A
之所以如此具体,是C
因为您始终可以C
使用 , 的单个参数进行A
调用
C
与类型推断一样具体A
:您始终可以使用sinceA
的参数调用任何内容(可以将其参数的类型推断为我们想要的任何内容)。的参数被视为一个所以推断为in并且它可以调用它。所以具体如。C
A
C
Seq[A]
T
Seq[A]
A
C
A
如果您更改A
为apply[T <: Int](x: T)
: 它会一直寻找最具体的那个,但这次类型推断找不到A
适用于C
' 参数 (a Seq
) 的方法,因为它不是 , 的子Int
类型A
最具体的也是如此。显然,如果您更改A
为apply(x: Int)
, 类型推断甚至不能做任何事情,也会发生同样的事情。
这也解释了为什么Test[Int](1)
成功调用一个参数版本。最后的两个替代方案是相同的,但是A
's 类型参数已绑定到Int
并且类型推断不能再将其更改为适合C
's 参数。
最后,应用相同的逻辑可以告诉您为什么Test(1,2)
可以正常工作:
B
与 一样具体C
:您始终可以C
使用B
的参数调用,
- 但
C
不如具体:再多的类型推断都无法B
将单个类型推断Seq
为采用两个参数的方法。
最具体的也是如此apply[T](x1: T, x2: T)
,没有错误。
基本上,对于 var-arg 和产生歧义的普通方法,它们需要具有相同数量的参数和一种在(至少)最后一个参数上欺骗类型推断的方法:
def apply[T](x1: T)
def apply[T](x: T*)
或者
def apply[T](x1: Int, x2:T)
def apply[T](x1: Int, x: T*)
等等...
编辑:起初我不确定在寻找特异性时是否将重复参数视为 aSeq[A]
或 a 。TupleX[...]
它绝对不是一个元组,自动元组与这些无关。