Побочная ошибка противоречивой ссылки в Scala 2.7.7 компиляторов/интерпретаторов?

Кто-либо может объяснить ошибку компиляции ниже? Интересно, если я изменяю тип возврата 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")

10
задан Aaron Novstrup 29 January 2010 в 01:08
поделиться

5 ответов

Что ж, это неоднозначно. Я полагаю, что семантика 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 не является более конкретной, потому что любой параметр, переданный одному, может быть передан и другому.

10
ответ дан 3 December 2019 в 15:22
поделиться

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

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

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

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

, чтобы разрешить двусмысленность.

5
ответ дан 3 December 2019 в 15:22
поделиться

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

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

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

4
ответ дан 3 December 2019 в 15:22
поделиться

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

3
ответ дан 3 December 2019 в 15:22
поделиться

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

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

Неуклюже, но будет сделать, поскольку Мартин и др., похоже, не скомпрометируют Scala, чтобы поддерживать varargs Java.

7
ответ дан 3 December 2019 в 15:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: