Ant*_*oly 16 scala overloading variadic-functions
给定以下具有重载版本的伴随对象apply:
object List {
  def apply[T](): List[T] = new Nil
  def apply[T](x1: T): List[T] = new Cons(x1, new Nil)
  def apply[T](x1: T, x2: T): List[T] = new Cons(x1, new Cons(x2, new Nil))
  def apply[T](elems: T*): List[T] = 
    elems.foldRight(List[T])((elem, l) => new Cons(elem, l))
}
Run Code Online (Sandbox Code Playgroud)
和两个实例
List(1) // Error - Ambiguity 
List('a', 'b') // Works fine
Run Code Online (Sandbox Code Playgroud)
scalac抱怨第一个实例化(对重载定义的模糊引用),因为单个参数和varargs方法都是同样具体的.
搜索stackoverflow我发现可以强制使用单个参数方法.List[Int](1)将使编译器使用def apply[T](x1: T).
我的问题是为什么第二个实例化def apply[T](x1: T, x2: T)没有额外的"提示" 匹配?换句话说,为什么两个参数方法比单个参数方法不是的varargs方法更具体?
要回答您的问题,我们需要了解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
}
Run Code Online (Sandbox Code Playgroud)
看看这三个电话:
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"
Run Code Online (Sandbox Code Playgroud)
让我们从第一个开始吧.首先,编译器查看每个参数的形状,这种类型基本上描述了参数是值还是函数.在这里,没有困难,1是一个非常正常,无聊的价值,它的形状是类型Nothing.
现在它有一个1类型的参数,Nothing并找到适用于它的所有替代方法.它找到了两个:
apply[T](x1: T):它接受一个无界类型的参数,因此它可以接收一个类型的参数Nothing,apply[T](elems: T*):它可以应用于0同一个无界类型的任何数量(包含)的参数,因此它可以接收单个类型的元素Nothing.它只有一个,它会停在那里并选择那一个.
第二步与上面的步骤相同,除了这次它用未定义的期望类型键入每个参数.基本上,它在这里查看左边的两个选项,并找出它们是否适用于1类型的参数A <: Int.没有运气,他们俩都是.如果你有两个变化apply[T](x1: T),以apply(x1: String)别去找另外一个,在这里将只有一个适用万般无奈之下,它会成功,并停止.
然后,编译器计算relative weight彼此剩余的每个备选方案.SLS说明了这一点
备选A相对于备选B 的相对权重是从0到2的数字,定义为总和
- 如果A与B具体相同,则为1,否则为0
 - 1如果A在类或对象中定义,该类或对象派生自定义B的类或对象,否则为0.
 
此时,必须有一个选项比其他选项具有更高的分数,或者存在歧义错误.我们可以忽略"已定义"部分,它们在同一个地方定义.
A是特定的C因为你可以随时调用C单个参数A,CA因为类型推断是特定的:你总是可以A使用参数调用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类型参数已被绑定Int,类型推断不能再将其更改为适合C的参数.
最后,应用相同的逻辑可以解释为什么Test(1,2)工作正常:
B是具体的C:你可以随时拨打C同B的论点,C是不具体为B:无类型推断的量将管理以适应单个Seq成采用一个方法2个参数.所以apply[T](x1: T, x2: T)最具体也没有错误.
基本上对于一个var-arg和一个产生歧义的常规方法,你需要有相同数量的参数和一种在(至少)最后一个参数上欺骗类型推断的方法:
def apply[T](x1: T)
def apply[T](x: T*)
Run Code Online (Sandbox Code Playgroud)
要么
def apply[T](x1: Int, x2:T)
def apply[T](x1: Int, x: T*)
Run Code Online (Sandbox Code Playgroud)
等等...
编辑:我一开始并不确定重复参数是否被视为a Seq[A]或者TupleX[...]在寻找特异性时.它绝对不是一个元组,自动元组与任何这一点都无关.
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           11906 次  |  
        
|   最近记录:  |