Принудительный одиночный аргумент в scala varargs

Учитывая класс java с двумя методами (взятый из mockito):

OngoingStubbing<T> thenReturn(T value);

OngoingStubbing<T> thenReturn(T value, T... values);

Если я вызываю из scala с

....thenReturn("something")

Я получаю сообщение об ошибке:

Description Resource    Path    Location    Type
ambiguous reference to overloaded definition, both method thenReturn in trait OngoingStubbing of type (x$1: java.lang.Object, x$2: <repeated...>[java.lang.Object])org.mockito.stubbing.OngoingStubbing[java.lang.Object] and  method thenReturn in trait OngoingStubbing of type (x$1: java.lang.Object)org.mockito.stubbing.OngoingStubbing[java.lang.Object] match argument types (java.lang.String)

И я не могу понять, как это исправить.

Ответ 1

Все ответы на все вопросы. Разница тонкая, но это не та же проблема, что и в связанном билете. Для этого требуется неразумная гимнастика, чтобы вызвать метод non-varargs. Для этого достаточно:

thenReturn[String]("something")

Или, если вы не хотели этого делать по какой-то причине, вам не нужен псевдоним типа и листинг. Вы можете напрямую ссылаться на структурный тип.

(this: { def thenReturn[T](s: T): OngoingStubbing[T] }).thenReturn("something")

Проблема здесь заключается в выводе типа на пересечении перегрузок и полиморфизма - один метод более конкретный, но scalac не выясняет, какой. Проблема в SI-2991 является подлинной двусмысленностью из-за взаимодействия между перегрузкой и преобразованием кортежей - не является более конкретным.

Ответ 2

Это известная проблема взаимодействия Scala -Java, хотя, к сожалению, она не относится к часто задаваемым вопросам. Здесь Scala билет, описывающий проблему. По сути, оба метода применимы, когда вы даете один аргумент, а в компиляторе Scala в настоящее время нет эвристики, чтобы решить, какой из них "более конкретный". Подход Алексея Романова, который всегда использует версию varargs, является хорошим способом:

thenReturn("something", Nil: _*)

Существует также вопрос который сталкивается с аналогичной проблемой с JCommander. Один из ответов дает умное обходное решение с использованием структурных типов. Этот подход будет использовать отражение за кулисами, поэтому вы можете или не захотите идти в этом направлении. Для вашего варианта использования это выглядит примерно так:

type useSingleArgVersion = { def thenReturn(value: AnyRef): OngoingStubbing }
(...).asInstanceOf[useSingleArgVersion].thenReturn("something")

Наконец, существует аналогичный вопрос с аналогичной проблемой с mokito. На самом деле это не обеспечивает каких-либо обходных решений, но он описывает проблему чуть более подробно, если вас интересует причина этого.

Ответ 3

Если вызов версии vararg допустим,

thenReturn("something", Nil: _*)

Невозможно придумать способ вызова метода без varargs прямо сейчас.

Ответ 4

Обходной путь довольно прост:

OngoingStubbing<T> thenReturn(T value);

OngoingStubbing<T> thenReturn(T value1, T valu2, T... values);

Нет функции "varargs должно быть не пустой".

Ответ 5

Я попробовал решение Стива и получил огромную ошибку компилятора, включая:

scala.tools.nsc.symtab.Types$TypeError: type mismatch;
 found   : scala.reflect.Manifest[Nothing]
 required: scala.reflect.ClassManifest[B]
Note: Nothing <: B, but trait ClassManifest is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: B`. (SLS 3.2.10)

Мне удалось заставить его работать с чем-то вроде:

thenReturn("something", Seq.empty[Object]: _*)

Ответ 6

Предполагая, что другие найдут этот вопрос при ошибке overloaded method value thenReturn with alternatives, я также хочу поделиться своим решением.

Вместо

when(field.getValue(isA(classOf[Record]))).thenReturn(value)

Я использую

doReturn(value).when(field).getValue(isA(classOf[Record]))

который устраняет двусмысленность в моем случае.