Can anyone explain the compile error below? Interestingly, if I change the return type of the get()
method to String
, the code compiles just fine. Note that the thenReturn
method has two overloads: a unary method and a varargs method that takes at least one argument. It seems to me that if the invocation is ambiguous here, then it would always be ambiguous.
有人能解释一下下面的编译错误吗?有趣的是,如果我将get()方法的返回类型更改为String,那么代码编译就很好。注意,thenReturn方法有两个重载:一元方法和至少接受一个参数的varargs方法。在我看来,如果调用在这里是不明确的,那么它总是不明确的。
More importantly, is there any way to resolve the ambiguity?
更重要的是,是否有办法解决这种不确定性?
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")
}
error: ambiguous reference to overloaded definition, both method thenReturn in trait OngoingStubbing of type
java.lang.Object,java.lang.Object*)org.mockito.stubbing.OngoingStubbing[java.lang.Object] and method thenReturn in trait OngoingStubbing of type (java.lang.Object)org.mockito.stubbing.OngoingStubbing[java.lang.Object] match argument types (java.lang.String) when(t.get()).thenReturn("a")错误:对重载定义的模糊引用,两种方法都返回java.lang. object类型的特征OngoingStubbing,java.lang. object *) org.mockito.stubb.ongoingstubbing [java.lang]。对象]和方法返回特征值OngoingStubbing of type (java.lang.Object)org.mockit . stubb. OngoingStubbing[java.lang]。对象]匹配参数类型(java.lang.String)时(t.get() .thenReturn(“a”)
5 个解决方案
#1
9
Well, it is ambiguous. I suppose Java semantics allow for it, and it might merit a ticket asking for Java semantics to be applied in Scala.
这是模棱两可的。我认为Java语义允许这样做,并且它可能需要在Scala中应用Java语义。
The source of the ambiguitity is this: a vararg parameter may receive any number of arguments, including 0. So, when you write thenReturn("a")
, do you mean to call the thenReturn
which receives a single argument, or do you mean to call the thenReturn
that receives one object plus a vararg, passing 0 arguments to the vararg?
这种模糊性的来源是:vararg参数可以接收任意数量的参数,包括0。那么,当您写入thenReturn(“a”)时,您是要调用接收单个参数的thenReturn,还是要调用接收一个对象加上vararg的thenReturn,将0个参数传递给vararg?
Now, what this kind of thing happens, Scala tries to find which method is "more specific". Anyone interested in the details should look up that in Scala's specification, but here is the explanation of what happens in this particular case:
现在,这种事情发生了,Scala试图找到“更具体”的方法。任何对细节感兴趣的人都应该在Scala的规范中查找,但是下面是对这个特殊情况的解释:
object t {
def f(x: AnyRef) = 1 // A
def f(x: AnyRef, xs: AnyRef*) = 2 // B
}
if you call
f("foo")
, both A and B are applicable. Which one is more specific?如果你调用f(“foo”),A和B都适用。哪个更具体?
- it is possible to call B with parameters of type
(AnyRef)
, so A is as specific as B.- 可以用类型(AnyRef)的参数调用B,所以A和B一样具体。
- it is possible to call A with parameters of type
(AnyRef, Seq[AnyRef])
thanks to tuple conversion,Tuple2[AnyRef, Seq[AnyRef]]
conforms toAnyRef
. So B is as specific as A. Since both are as specific as the other, the reference to f is ambiguous.- 可以调用类型参数(AnyRef, Seq[AnyRef]),由于tuple转换,Tuple2[AnyRef, Seq[AnyRef]]符合AnyRef。所以B和a一样具体,因为两者都和另一个一样具体,所以f的引用是不明确的。
As to the "tuple conversion" thing, it is one of the most obscure syntactic sugars of Scala. If you make a call f(a, b)
, where a
and b
have types A
and B
, and there is no f
accepting (A, B)
but there is an f
which accepts (Tuple2(A, B))
, then the parameters (a, b)
will be converted into a tuple.
至于“元组转换”,它是Scala最晦涩的语法糖之一。如果你调用f(a, b),其中a和b有a和b,并且没有f(a, b),但是有一个f可以接受(Tuple2(a, b)),那么参数(a, b)将被转换成一个元组。
For example:
例如:
scala> def f(t: Tuple2[Int, Int]) = t._1 + t._2
f: (t: (Int, Int))Int
scala> f(1,2)
res0: Int = 3
Now, there is no tuple conversion going on when thenReturn("a")
is called. That is not the problem. The problem is that, given that tuple conversion is possible, neither version of thenReturn
is more specific, because any parameter passed to one could be passed to the other as well.
现在,当调用thenReturn(“a”)时,不会进行元组转换。这不是问题所在。问题是,考虑到元组转换是可能的,所以没有哪个版本的返回更具体,因为传递给一个参数的任何参数都可以传递给另一个。
#2
6
Well, I figured out how to resolve the ambiguity (seems kind of obvious in retrospect):
我找到了解决歧义的方法(回想起来似乎有点明显):
when(t.get()).thenReturn("a", Array[Object](): _*)
As Andreas noted, if the ambiguous method requires a null reference rather than an empty array, you can use something like
正如Andreas所指出的,如果歧义方法需要一个空引用而不是一个空数组,那么可以使用以下方法
v.overloadedMethod(arg0, null.asInstanceOf[Array[Object]]: _*)
to resolve the ambiguity.
解决歧义。
#3
6
In the specific case of Mockito, it's possible to use the alternate API methods designed for use with void methods:
在Mockito的具体情况下,可以使用为void方法设计的替代API方法:
doReturn("a").when(t).get()
Clunky, but it'll have to do, as Martin et al don't seem likely to compromise Scala in order to support Java's varargs.
虽然有点笨拙,但它必须这么做,因为Martin等人似乎不太可能为了支持Java的varargs而放弃Scala。
#4
4
If you look at the standard library APIs you'll see this issue handled like this:
如果你看看标准库api,你会发现这个问题是这样处理的:
def meth(t1: Thing): OtherThing = { ... }
def meth(t1: Thing, t2: Thing, ts: Thing*): OtherThing = { ... }
By doing this, no call (with at least one Thing parameter) is ambiguous without extra fluff like Array[Thing](): _*
.
通过这样做,没有调用(至少有一个Thing参数)是不明确的,而没有像Array[Thing](): _*这样的额外错误。
#5
3
I had a similar problem using Oval (oval.sf.net) trying to call it's validate()-method.
我在使用Oval (oval.sf.net)尝试调用它的validate()-method时遇到了类似的问题。
Oval defines 2 validate() methods:
Oval定义了2个validate()方法:
public List<ConstraintViolation> validate(final Object validatedObject)
public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles)
Trying this from Scala: validator.validate(value)
produces the following compiler-error:
在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 needs the varargs-parameter to be null, not an empty-array, so I finally got it to work with this:
Oval需要varargs参数为null,而不是空数组,因此我最终让它处理如下:
validator.validate(value, null.asInstanceOf[Array[String]]: _*)
验证器。验证(值为null。asInstanceOf[Array[String]]:_ *)
#1
9
Well, it is ambiguous. I suppose Java semantics allow for it, and it might merit a ticket asking for Java semantics to be applied in Scala.
这是模棱两可的。我认为Java语义允许这样做,并且它可能需要在Scala中应用Java语义。
The source of the ambiguitity is this: a vararg parameter may receive any number of arguments, including 0. So, when you write thenReturn("a")
, do you mean to call the thenReturn
which receives a single argument, or do you mean to call the thenReturn
that receives one object plus a vararg, passing 0 arguments to the vararg?
这种模糊性的来源是:vararg参数可以接收任意数量的参数,包括0。那么,当您写入thenReturn(“a”)时,您是要调用接收单个参数的thenReturn,还是要调用接收一个对象加上vararg的thenReturn,将0个参数传递给vararg?
Now, what this kind of thing happens, Scala tries to find which method is "more specific". Anyone interested in the details should look up that in Scala's specification, but here is the explanation of what happens in this particular case:
现在,这种事情发生了,Scala试图找到“更具体”的方法。任何对细节感兴趣的人都应该在Scala的规范中查找,但是下面是对这个特殊情况的解释:
object t {
def f(x: AnyRef) = 1 // A
def f(x: AnyRef, xs: AnyRef*) = 2 // B
}
if you call
f("foo")
, both A and B are applicable. Which one is more specific?如果你调用f(“foo”),A和B都适用。哪个更具体?
- it is possible to call B with parameters of type
(AnyRef)
, so A is as specific as B.- 可以用类型(AnyRef)的参数调用B,所以A和B一样具体。
- it is possible to call A with parameters of type
(AnyRef, Seq[AnyRef])
thanks to tuple conversion,Tuple2[AnyRef, Seq[AnyRef]]
conforms toAnyRef
. So B is as specific as A. Since both are as specific as the other, the reference to f is ambiguous.- 可以调用类型参数(AnyRef, Seq[AnyRef]),由于tuple转换,Tuple2[AnyRef, Seq[AnyRef]]符合AnyRef。所以B和a一样具体,因为两者都和另一个一样具体,所以f的引用是不明确的。
As to the "tuple conversion" thing, it is one of the most obscure syntactic sugars of Scala. If you make a call f(a, b)
, where a
and b
have types A
and B
, and there is no f
accepting (A, B)
but there is an f
which accepts (Tuple2(A, B))
, then the parameters (a, b)
will be converted into a tuple.
至于“元组转换”,它是Scala最晦涩的语法糖之一。如果你调用f(a, b),其中a和b有a和b,并且没有f(a, b),但是有一个f可以接受(Tuple2(a, b)),那么参数(a, b)将被转换成一个元组。
For example:
例如:
scala> def f(t: Tuple2[Int, Int]) = t._1 + t._2
f: (t: (Int, Int))Int
scala> f(1,2)
res0: Int = 3
Now, there is no tuple conversion going on when thenReturn("a")
is called. That is not the problem. The problem is that, given that tuple conversion is possible, neither version of thenReturn
is more specific, because any parameter passed to one could be passed to the other as well.
现在,当调用thenReturn(“a”)时,不会进行元组转换。这不是问题所在。问题是,考虑到元组转换是可能的,所以没有哪个版本的返回更具体,因为传递给一个参数的任何参数都可以传递给另一个。
#2
6
Well, I figured out how to resolve the ambiguity (seems kind of obvious in retrospect):
我找到了解决歧义的方法(回想起来似乎有点明显):
when(t.get()).thenReturn("a", Array[Object](): _*)
As Andreas noted, if the ambiguous method requires a null reference rather than an empty array, you can use something like
正如Andreas所指出的,如果歧义方法需要一个空引用而不是一个空数组,那么可以使用以下方法
v.overloadedMethod(arg0, null.asInstanceOf[Array[Object]]: _*)
to resolve the ambiguity.
解决歧义。
#3
6
In the specific case of Mockito, it's possible to use the alternate API methods designed for use with void methods:
在Mockito的具体情况下,可以使用为void方法设计的替代API方法:
doReturn("a").when(t).get()
Clunky, but it'll have to do, as Martin et al don't seem likely to compromise Scala in order to support Java's varargs.
虽然有点笨拙,但它必须这么做,因为Martin等人似乎不太可能为了支持Java的varargs而放弃Scala。
#4
4
If you look at the standard library APIs you'll see this issue handled like this:
如果你看看标准库api,你会发现这个问题是这样处理的:
def meth(t1: Thing): OtherThing = { ... }
def meth(t1: Thing, t2: Thing, ts: Thing*): OtherThing = { ... }
By doing this, no call (with at least one Thing parameter) is ambiguous without extra fluff like Array[Thing](): _*
.
通过这样做,没有调用(至少有一个Thing参数)是不明确的,而没有像Array[Thing](): _*这样的额外错误。
#5
3
I had a similar problem using Oval (oval.sf.net) trying to call it's validate()-method.
我在使用Oval (oval.sf.net)尝试调用它的validate()-method时遇到了类似的问题。
Oval defines 2 validate() methods:
Oval定义了2个validate()方法:
public List<ConstraintViolation> validate(final Object validatedObject)
public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles)
Trying this from Scala: validator.validate(value)
produces the following compiler-error:
在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 needs the varargs-parameter to be null, not an empty-array, so I finally got it to work with this:
Oval需要varargs参数为null,而不是空数组,因此我最终让它处理如下:
validator.validate(value, null.asInstanceOf[Array[String]]: _*)
验证器。验证(值为null。asInstanceOf[Array[String]]:_ *)