Принудительный одиночный аргумент в 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]))
который устраняет двусмысленность в моем случае.