【问题标题】:Spurious ambiguous reference error in Scala 2.7.7 compiler/interpreter?Scala 2.7.7 编译器/解释器中的虚假模糊引用错误?
【发布时间】:2010-01-29 01:08:28
【问题描述】:

谁能解释下面的编译错误?有趣的是,如果我将get() 方法的返回类型更改为String,代码编译得很好。请注意,thenReturn 方法有两个重载:一元方法和至少采用一个参数的可变参数方法。在我看来,如果这里的调用是模棱两可的,那么它总是模棱两可的。

更重要的是,有什么办法可以解决歧义?

import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._

trait Thing { 
   def get(): java.lang.Object 
}

new MockitoSugar {   
   val t = mock[Thing]  

   when(t.get()).thenReturn("a")  
}

错误:对重载定义的模糊引用, 两种方法 thenReturn 在 trait OngoingStubbing 类型
java.lang.Object,java.lang.Object*)org.mockito.stubbing.OngoingStubbing[java.lang.Object] 和方法 thenReturn 在 trait OngoingStubbing 类型 (java.lang.Object)org.mockito.stubbing.OngoingStubbing[java.lang.Object] 匹配参数类型(java.lang.String) when(t.get()).thenReturn("a")

【问题讨论】:

  • 我为此开了一张票,因为我发现 Scala 甚至与自己不一致。票务lampsvn.epfl.ch/trac/scala/ticket/2991.
  • 票被关闭为无效,现在有一个关于发生了什么的解释,我将复制到我自己的答案中。目前,我认为改变的可能性不大。

标签: scala compiler-errors


【解决方案1】:

嗯,这是模棱两可的。我想 Java 语义允许这样做,并且可能值得一张票要求在 Scala 中应用 Java 语义。

歧义的来源是:一个可变参数可以接收任意数量的参数,包括0。所以,当你写thenReturn("a")时,你的意思是调用接收单个参数的thenReturn,还是做你的意思是调用thenReturn,它接收一个对象加上一个可变参数,将0个参数传递给可变参数?

现在,这种事情发生了什么,Scala 试图找出哪种方法“更具体”。任何对细节感兴趣的人都应该在 Scala 的规范中查找,但这里是对这种特殊情况下发生的情况的解释:

object t {
  def f(x: AnyRef) = 1 // A
  def f(x: AnyRef, xs: AnyRef*) = 2 // B
}

如果你打电话给f("foo"),A 和 B 适用。哪个更 具体的?

  • 可以用(AnyRef)类型的参数调用B,所以A是 和 B 一样具体。
  • 借助元组,可以使用 (AnyRef, Seq[AnyRef]) 类型的参数调用 A 转换,Tuple2[AnyRef, Seq[AnyRef]] 符合 AnyRef。所以 B 与 A 一样具体。因为两者都是 与另一个一样具体, 对 f 的引用不明确。

至于“元组转换”,它是 Scala 最晦涩的语法糖之一。如果您拨打电话f(a, b),其中abAB 类型,并且没有f 接受(A, B),但有一个f 接受(Tuple2(A, B)) , 那么参数(a, b) 会被转换成一个元组。

例如:

scala> def f(t: Tuple2[Int, Int]) = t._1 + t._2
f: (t: (Int, Int))Int

scala> f(1,2)
res0: Int = 3

现在,在调用 thenReturn("a") 时不会进行元组转换。那不是问题。问题是,考虑到元组转换是可能的,thenReturn 的任何一个版本都不是更具体的,因为传递给一个的任何参数也可以传递给另一个。

【讨论】:

    【解决方案2】:

    在 Mockito 的特定情况下,可以使用为 void 方法设计的备用 API 方法:

    doReturn("a").when(t).get()
    

    笨拙,但必须这样做,因为 Martin 等人似乎不太可能为了支持 Java 的可变参数而妥协 Scala。

    【讨论】:

      【解决方案3】:

      嗯,我想出了如何解决歧义(回想起来似乎很明显):

      when(t.get()).thenReturn("a", Array[Object](): _*)
      

      正如 Andreas 所指出的,如果模棱两可的方法需要空引用而不是空数组,您可以使用类似

      v.overloadedMethod(arg0, null.asInstanceOf[Array[Object]]: _*)
      

      解决歧义。

      【讨论】:

      • 这是实际的答案(尽管 Daniel Sobral 的答案当然非常丰富)。虽然,我发现我必须提供返回类型的 Array 而不仅仅是 Array[Object]。
      【解决方案4】:

      如果您查看标准库 API,您会发现此问题的处理方式如下:

      def meth(t1: Thing): OtherThing = { ... }
      def meth(t1: Thing, t2: Thing, ts: Thing*): OtherThing = { ... }
      

      通过这样做,没有像 Array[Thing](): _* 这样的额外绒毛,任何调用(至少带有一个 Thing 参数)都是模棱两可的。

      【讨论】:

      • 这似乎是一种更好的编写方式。不幸的是,thenReturn 是在第三方 Java 库 (Mockito) 中定义的。正如 Daniel 指出的那样,Java 解决了非可变参数方法的歧义,所以我什至不能称它为库中的错误。
      • 现在我理解了这个问题,在传递两个参数时很可能容易出现歧义。
      【解决方案5】:

      我在使用 Oval (oval.sf.net) 时遇到了类似的问题,试图将其称为 validate() 方法。

      Oval 定义了 2 个 validate() 方法:

      public List<ConstraintViolation> validate(final Object validatedObject)
      public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles)
      

      在 Scala 中尝试: validator.validate(value) 产生以下编译器错误:

      both method validate in class Validator of type (x$1: Any,x$2: <repeated...>[java.lang.String])java.util.List[net.sf.oval.ConstraintViolation]                                                          
      and  method validate in class Validator of type (x$1: Any)java.util.List[net.sf.oval.ConstraintViolation]                                                                                               
      match argument types (T)                                                                                                                                                                                
              var violations = validator.validate(entity);                                                                                                                                                    
      

      Oval 需要 varargs-parameter 为 null,而不是空数组,所以我终于可以使用它了:

      validator.validate(value, null.asInstanceOf[Array[String]]: _*)

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      相关资源
      最近更新 更多