Кто-либо может объяснить ошибку компиляции ниже? Интересно, если я изменяю тип возврата 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")
}
ошибка: противоречивая ссылка к перегруженному определению, обоим методам thenReturn в черте OngoingStubbing типа
java.lang. Объект, java.lang. Объект*) org.mockito.stubbing. OngoingStubbing [java.lang. Объект] и метод thenReturn в черте OngoingStubbing типа (java.lang. Объект) org.mockito.stubbing. OngoingStubbing [java.lang. Объект] соответствует типам аргумента (java.lang. Строка), когда (t.get ()) .thenReturn ("a")
Что ж, это неоднозначно. Я полагаю, что семантика 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")
, как A, так и B { {1}} применимы. Какой из них более ?
- можно вызвать B с параметрами типа
(AnyRef)
, поэтому A так же специфичен, как B.- можно вызвать A с параметрами типа
(AnyRef, Seq [AnyRef])
благодаря преобразованию кортежа ,Tuple2 [AnyRef, Seq [AnyRef]]
соответствуетAnyRef
. Итак, B так же специфичен, как и A. Поскольку оба так же специфичны, как и другие, ссылка на 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
не является более конкретной, потому что любой параметр, переданный одному, может быть передан и другому.
Ну, я выяснил, как разрешить двусмысленность (кажется вида очевидным в ретроспективе):
when(t.get()).thenReturn("a", Array[Object](): _*)
Как отметил Андреас, если неоднозначный метод требует нулевой ссылки, а не пустой массив, Вы можете использовать что-то вроде
v.overloadedMethod(arg0, null.asInstanceOf[Array[Object]]: _*)
, чтобы разрешить двусмысленность.
Если вы посмотрите на стандартную библиотеку API, вы увидите, что эта проблема обрабатывается такая:
def meth(t1: Thing): OtherThing = { ... }
def meth(t1: Thing, t2: Thing, ts: Thing*): OtherThing = { ... }
, делая это, нет вызова (с по меньшей мере одним параметром) неоднозначен без дополнительных пухов, таких как ] (): _ *
.
У меня была похожая проблема с использованием Oval (oval.sf.net) при попытке вызвать метод validate()-method.
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);
Oval нуждается в том, чтобы параметр varargs был нулевым, а не пустым массивом, так что я наконец-то заставил его работать с этим:
validator.validate(value, null. asInstanceOf[Array[String]]: _*)
В конкретном случае Mockito можно использовать альтернативные методы API, разработанные для использования с методами void:
doReturn("a").when(t).get()
Неуклюже, но будет сделать, поскольку Мартин и др., похоже, не скомпрометируют Scala, чтобы поддерживать varargs Java.