2010-01-29 9 views
7

Quelqu'un peut-il expliquer l'erreur de compilation ci-dessous? Fait intéressant, si je change le type de retour de la méthode get() à String, le code compile très bien. Notez que la méthode thenReturn a deux surcharges: une méthode unaire et une méthode varargs qui prend au moins un argument. Il me semble que si l'invocation est ambiguë ici, alors elle serait toujours ambiguë.Fausse erreur de référence ambigu dans le compilateur/interpréteur Scala 2.7.7?

Plus important encore, existe-t-il un moyen de résoudre l'ambiguïté?

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

J'ai ouvert un ticket à ce sujet, car j'ai découvert que Scala n'était même pas cohérente avec elle-même. Billet https://lampsvn.epfl.ch/trac/scala/ticket/2991. –

+0

Le ticket a été fermé comme non valide, et il y a maintenant une explication de ce qui se passe, que je vais copier à ma propre réponse. À l'heure actuelle, je ne pense pas qu'il y ait beaucoup de chance de changer cela. –

Répondre

9

Eh bien, c'est ambigu. Je suppose que la sémantique Java le permet, et cela pourrait mériter un ticket demandant que la sémantique Java soit appliquée dans Scala.

La source du ambiguitity est la suivante: un paramètre vararg peut recevoir un certain nombre d'arguments, y compris 0. Donc, quand vous écrivez thenReturn("a"), voulez-vous dire d'appeler le thenReturn qui reçoit un seul argument, ou voulez-vous dire à appelez le thenReturn qui reçoit un objet plus un vararg, en passant 0 arguments à la vararg? Maintenant, ce genre de chose arrive, Scala essaye de trouver quelle méthode est "plus spécifique". Toute personne intéressée par les détails devraient lever les yeux que dans le cahier des charges de Scala, mais voici l'explication de ce qui se passe dans ce cas particulier:

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.

Quant à la chose « conversion tuple », il est l'un des les sucres syntaxiques les plus obscurs de Scala. Si vous faites un appel f(a, b), où a et b ont des types A et B, et il n'y a pas f accepter (A, B) mais il y a un f qui accepte (Tuple2(A, B)), les paramètres (a, b) seront convertis en un tuple.

Par exemple:

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

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

Maintenant, il n'y a pas de conversion tuple passe quand thenReturn("a") est appelé. Ce n'est pas le problème. Le problème est que, étant donné que la conversion en tuple est possible, aucune des deux versions de thenReturn n'est plus spécifique, car tout paramètre transmis à l'un peut également être transmis à l'autre.

6

Eh bien, je me suis dit comment résoudre l'ambiguïté (semble assez évident rétrospectivement):

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

Comme Andreas a noté, si la méthode ambiguë exige une référence nulle et non un tableau vide, vous pouvez utiliser quelque chose comme

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

pour résoudre l'ambiguïté.

+1

Ceci est la réponse réelle (bien que Daniel Sobral est bien sûr très instructif). Bien que, j'ai trouvé que je devais fournir un tableau du type de retour plutôt que simplement Array [Object]. – gladed

4

Si vous regardez les API de la bibliothèque standard, vous verrez cette question traitée comme ceci:

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

En faisant cela, aucun appel (avec au moins un paramètre Thing) est ambigu sans peluches supplémentaire comme Array[Thing](): _* .

+0

Cela semble être une meilleure façon de l'écrire. Malheureusement, 'thenReturn' est défini dans une bibliothèque Java tierce (Mockito). Comme Daniel a souligné que Java résout l'ambiguïté de la méthode non-varargs, je ne peux même pas l'appeler un bug dans la bibliothèque. –

+0

Et il est fort probable qu'il soit vulnérable à l'ambiguïté en passant deux paramètres, maintenant que je comprends le problème. –

3

J'ai eu un problème similaire en utilisant Oval (oval.sf.net) en essayant d'appeler sa méthode validate().

ovale définit deux validate() méthodes:

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

essayer cette Scala: validator.validate(value) produit le compilateur d'erreur suivant:

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

ovale a besoin de la varargs-paramètre à être nul , pas un tableau vide, donc je l'ai finalement eu à travailler avec ceci:

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

+0

Merci d'avoir ajouté votre valise, Andreas. Je l'ai documenté sur le ticket de trac de Daniel (https://lampsvn.epfl.ch/trac/scala/ticket/2991). –

6

Dans le cas particulier de Mockito, il est possible d'utiliser les méthodes de l'API de remplacement conçus pour être utilisés avec des méthodes vides:

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

Maladroit, mais il faudra le faire, comme Martin et al don » Il semble que cela risque de compromettre Scala pour supporter les varargs de Java.

Questions connexes