Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force single argument in scala varargs

Tags:

mockito

scala

Given a java class with two methods (taken from mockito):

OngoingStubbing<T> thenReturn(T value);

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

If I invoke from scala with

....thenReturn("something")

I get an error:

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)

And I cannot figure out how to fix this.

like image 430
sksamuel Avatar asked Nov 13 '12 10:11

sksamuel


4 Answers

This is a known Scala-Java interoperability problem, though it's unfortunately not in the FAQ. Here's the Scala ticket describing the problem. Essentially, both methods are applicable when you give a single argument, and the Scala compiler currently doesn't have any heuristic to decide which one is "more specific". Alexey Romanov's approach to always use the varargs version is a good workaround:

thenReturn("something", Nil: _*)

There is also a question running into a similar problem with JCommander. One of the answers there gives a clever workaround using structural types. This approach will use reflection behind the scenes, so you may or may not want to go that direction. For your use case, it would look something like:

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

Finally, there is a similar question running into a similar problem with mokito. It doesn't really provide any workarounds, but it does describe the problem in a bit more detail, if you're interested in the reason this happens.

like image 79
Steve Avatar answered Nov 13 '22 18:11

Steve


If calling the vararg version is acceptable,

thenReturn("something", Nil: _*)

Can't think of a way to call the method without varargs right now.

like image 35
Alexey Romanov Avatar answered Nov 13 '22 19:11

Alexey Romanov


These answers are all to the wrong question. The difference is subtle, but this is not the same issue as the one in the linked ticket. That one does require unreasonable gymnastics to call the non-varargs method. For this one, the following is enough.

thenReturn[String]("something")

Or, if you didn't want to do that for some reason, you don't need the type alias and the cast. You can use a structural type ascription directly.

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

The issue here is type inference at the intersection of overloading and polymorphism - one method is more specific, but scalac doesn't figure out which. The issue in SI-2991 is genuine ambiguity due to an interaction between overloading and tuple conversion - neither is more specific.

like image 13
psp Avatar answered Nov 13 '22 19:11

psp


Assuming others will find this question when having the overloaded method value thenReturn with alternatives error, I want to share my solution as well.

Instead of

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

I use

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

which resolves the disambiguity in my case.

like image 2
DaniRey Avatar answered Nov 13 '22 17:11

DaniRey