2010-01-29 86 views
7

任何人都可以解釋下面的編譯錯誤?有趣的是,如果我將get()方法的返回類型更改爲String,代碼編譯就好了。請注意,thenReturn方法有兩個重載:一個一元方法和一個至少需要一個參數的可變參數方法。在我看來,如果調用在這裏不明確,那麼它總是含糊不清。Scala 2.7.7編譯器/解釋器中的僞歧義引用錯誤?

更重要的是,有沒有什麼辦法來解決歧義?

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")

+0

我打開了一張關於此的票據,因爲我發現斯卡拉甚至沒有與自己保持一致。門票https://lampsvn.epfl.ch/trac/scala/ticket/2991。 – 2010-01-29 12:01:04

+0

門票被關閉爲無效,現在有關於正在發生什麼的解釋,我將複製到我自己的答案中。目前,我認爲這個變化的機會不大。 – 2010-02-03 13:09:31

回答

9

嗯,這是不明確的。我認爲Java語義允許它,並且可能需要一個請求在Java語義中應用Scala的票證。

歧義的來源是:可變參數可能會收到任意數量的參數,包括0.所以,當你寫thenReturn("a"),你的意思是調用thenReturn接收單個參數,或者你的意思是調用thenReturn接收一個對象加可變參數,將0個參數傳遞給可變參數?

現在,這是什麼樣的事情發生,斯卡拉試圖找出哪一種方法是「更具體」。任何有興趣的細節應該查找,在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?

  • it is possible to call B with parameters of type (AnyRef) , so A is as specific as B.
  • it is possible to call A with parameters of type (AnyRef, Seq[AnyRef]) thanks to tuple conversion, Tuple2[AnyRef, Seq[AnyRef]] conforms to AnyRef . So B is as specific as A. Since both are as specific as the other, the reference to f is ambiguous.

至於「元組轉換」的東西,它的一個斯卡拉大多數模糊的語法糖。如果您撥打一個電話f(a, b),其中ab有類型AB,並沒有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這兩個版本都不是特定的,因爲傳遞給一個參數的任何參數都可以傳遞給另一個參數。

6

好吧,我想通了,如何解決歧義(似乎有點明顯回想起來):

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

如安德烈亞斯指出,如果不明確的方法需要一個空引用,而不是一個空陣列,你可以使用類似

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

來解決歧義。

+1

這是實際的答案(儘管Daniel Sobral的當然非常豐富)。雖然,我發現我必須提供返回類型的數組,而不僅僅是Array [Object]。 – gladed 2012-03-12 00:56:55

4

如果你看一下標準庫API,你會看到這個問題是這樣處理的:

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

通過這樣做,沒有呼叫(至少有一個件事參數)是不喜歡Array[Thing](): _*額外的絨毛曖昧。

+0

這似乎是一個更好的寫法。不幸的是,'thenReturn'是在第三方Java庫(Mockito)中定義的。正如Daniel指出的,Java解決了non-varargs方法的含糊性,所以我甚至不能稱它爲庫中的一個bug。 – 2010-01-29 19:27:07

+0

現在我明白了這個問題,當傳遞兩個參數時,它很可能容易模棱兩可。 – 2010-02-03 13:08:27

3

我有一個類似的問題,使用橢圓形(oval.sf.net)試圖調用它的validate() - 方法。

橢圓定義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);                                      

橢圓需要可變參數參數爲空,而不是空陣列,所以我終於得到它與此工作:

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

+0

感謝您加入您的案例,Andreas。我把它記錄在Daniel的trac票上(https://lampsvn.epfl.ch/trac/scala/ticket/2991)。 – 2010-02-01 22:07:53

6

在的Mockito的特定情況下,它可以使用專爲使用無效的方法替代API方法:

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

笨重,但它必須做,馬丁等人不要」爲了支持Java的可變參數,似乎可能會損害Scala。