Поддельная двусмысленная ошибка в компиляторе / интерпретаторе Scala 2.7.7? - PullRequest
9 голосов
/ 29 января 2010

Кто-нибудь может объяснить ошибку компиляции ниже? Интересно, что если я изменю тип возврата метода get() на String, код скомпилируется просто отлично. Обратите внимание, что метод thenReturn имеет две перегрузки: унарный метод и метод varargs, который принимает хотя бы один аргумент. Мне кажется, что если вызов здесь неоднозначен, то он всегда будет неоднозначным.

Что еще более важно, есть ли способ устранить неоднозначность?

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

ошибка: неоднозначная ссылка на перегруженное определение, оба метода затем возвращаются в черту OngoingStubbing типа
java.lang.Object, java.lang.Object *) org.mockito.stubbing.OngoingStubbing [java.lang.Object] и метод thenReturn в признаке OngoingStubbing типа (Java.lang.Object) org.mockito.stubbing.OngoingStubbing [java.lang.Object] сопоставлять типы аргументов (java.lang.String) когда (t.get ()). thenReturn ( "а")

Ответы [ 5 ]

9 голосов
/ 29 января 2010

Ну, это неоднозначно. Я полагаю, что семантика Java допускает это, и это может стоить запроса на применение семантики Java в Scala.

Источник неоднозначности заключается в следующем: параметр vararg может получить любое количество аргументов, включая 0. Поэтому, когда вы пишете thenReturn("a"), вы хотите вызвать thenReturn, который получает один аргумент, или сделать Вы хотите вызвать thenReturn, который получает один объект плюс vararg, передавая 0 аргументов vararg?

Теперь, что происходит с такими вещами, Scala пытается найти, какой метод является «более конкретным». Любой, кто интересуется деталями, должен посмотреть это в спецификации Scala, но вот объяснение того, что происходит в данном конкретном случае:

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

если вы позвоните f("foo"), то и А, и В применимы. Какой из них больше конкретный?

  • можно вызвать B с параметрами типа (AnyRef), поэтому A так же конкретно, как B.
  • можно вызвать A с параметрами типа (AnyRef, Seq[AnyRef]) благодаря кортежу преобразование Tuple2[AnyRef, Seq[AnyRef]] соответствует AnyRef. Так B так же специфичен, как и А. Поскольку оба так же, как и другие, ссылка на f неоднозначна.

Что касается «преобразования кортежей», то это один из самых малоизвестных синтаксических сахаров в Scala. Если вы делаете вызов f(a, b), где a и b имеют типы A и B, и 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 не является более конкретной, поскольку любой параметр, передаваемый одному, может передаваться и другому.

7 голосов
/ 17 февраля 2010

В конкретном случае Mockito можно использовать альтернативные методы API, разработанные для использования с пустыми методами:

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

Неуклюжий, но с этим придется смириться, поскольку Мартин и его коллеги вряд ли скомпрометируют Scala для поддержки varargs Java.

5 голосов
/ 29 января 2010

Ну, я разобрался, как устранить неоднозначность (кажется довольно очевидной в ретроспективе):

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

Как отметил Андреас, если для неоднозначного метода требуется пустая ссылка, а не пустой массив, вы можете использовать что-то вроде

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

чтобы устранить неоднозначность.

4 голосов
/ 29 января 2010

Если вы посмотрите на стандартные библиотечные API, вы увидите, что эта проблема решена так:

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

При этом ни один вызов (хотя бы с одним параметром Thing) не будет неоднозначным без лишнего пуха, как Array[Thing](): _*.

3 голосов
/ 31 января 2010

У меня была похожая проблема при использовании 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);                                                                                                                                                    

Для овала необходимо, чтобы параметр varargs был нулевым, а не пустым массивом, поэтому я наконец-то получил его для работы с этим:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...