2010-01-29 15 views
7

¿Alguien puede explicar el error de compilación a continuación? Curiosamente, si cambio el tipo de devolución del método get() a String, el código se compila muy bien. Tenga en cuenta que el método thenReturn tiene dos sobrecargas: un método unario y un método varargs que toma al menos un argumento. Me parece que si la invocación es ambigua aquí, entonces siempre sería ambigua.Error de referencia ambiguo falso en el compilador/intérprete de Scala 2.7.7?

Más importante aún, ¿hay alguna manera de resolver la ambigüedad?

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

Abrí un boleto sobre esto, ya que descubrí que Scala ni siquiera era coherente consigo mismo. Entrada https://lampsvn.epfl.ch/trac/scala/ticket/2991. –

+0

El ticket se cerró como no válido, y ahora hay una explicación de lo que está sucediendo, que copiaré en mi propia respuesta. En este momento, no creo que haya muchas posibilidades de un cambio en esto. –

Respuesta

9

Bueno, es ambiguo. Supongo que la semántica de Java lo permite, y podría merecer un ticket pidiendo que la semántica de Java se aplique en Scala.

La fuente de la ambiguitity es la siguiente: un parámetro vararg puede recibir cualquier número de argumentos, incluyendo 0. Por lo tanto, cuando se escribe thenReturn("a"), qué se refiere a llamar a la thenReturn que recibe un solo argumento, o qué se refiere a llame al thenReturn que recibe un objeto más un vararg, pasando 0 argumentos a la vararg?

Ahora, qué sucede esto, Scala intenta encontrar qué método es "más específico". Cualquier persona interesada en los detalles debe mirar hacia arriba que en la especificación de Scala, pero aquí es la explicación de lo que sucede en este caso particular:

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.

En cuanto a la cosa "conversión tupla", es una de las la mayoría de los azúcares sintácticos oscuros de Scala. Si realiza una llamada f(a, b), donde a y b tienen tipos A y B, y no hay f aceptar (A, B) pero hay una f que acepta (Tuple2(A, B)), entonces los parámetros (a, b) se convertirán en una tupla.

Por ejemplo:

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

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

Ahora, no hay conversión tupla pasando cuando thenReturn("a") se llama. Ese no es el problema. El problema es que, dado que la conversión de tupla es posible, ninguna de las versiones de thenReturn es más específica, porque cualquier parámetro pasado a uno podría pasarse al otro también.

6

Bueno, me di cuenta de cómo resolver la ambigüedad (parece un poco obvio en retrospectiva):

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

Como se señaló Andreas, si el método ambigua requiere una referencia nula en lugar de una matriz vacía, puede usar algo como

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

para resolver la ambigüedad.

+1

Esta es la respuesta real (aunque la de Daniel Sobral es, por supuesto, muy informativa). Sin embargo, descubrí que tenía que proporcionar una matriz del tipo de devolución en lugar de solo Matriz [Objeto]. – gladed

4

Si nos fijamos en las API de la biblioteca estándar verá este tema se maneja de esta manera:

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

Al hacer esto, no hay ninguna llamada (con al menos un parámetro cosa) es ambigua y sin pelusa extra como Array[Thing](): _* .

+0

Eso parece una mejor manera de escribirlo. Desafortunadamente, 'thenReturn' se define en una biblioteca de Java de terceros (Mockito). Como Daniel señaló que Java resuelve la ambigüedad del método no varargs, así que ni siquiera puedo llamarlo un error en la biblioteca. –

+0

Y es muy probable que sea vulnerable a la ambigüedad al pasar dos parámetros, ahora que entiendo el problema. –

3

Tuve un problema similar al usar Oval (oval.sf.net) tratando de llamarlo método validate().

Oval define 2 validate() métodos:

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

Tratando esto desde Scala: validator.validate(value) produce los siguientes compilador-error:

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 necesita la varargs-parámetro a ser nulo , no una matriz vacía, así que finalmente lo tengo que trabajar con esto:

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

+0

Gracias por agregar su caso, Andreas. Lo documenté en el boleto de Trac de Daniel (https://lampsvn.epfl.ch/trac/scala/ticket/2991) también. –

6

En el caso específico de Mockito, es posible utilizar los métodos de la API alternativos diseñados para su uso con métodos vacíos:

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

torpe, pero se tendrá que hacer, como Martin et al don' Parece probable que comprometa a Scala para admitir los varargs de Java.

Cuestiones relacionadas