Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mockito: How to match varargs in java 8?

I'm working on migrating a project from java 7 to 8, and have gotten a compilation error in a Mockito "when" case I'm having a hard time tracking down:

when(queryRunner.query(any(String.class), any(ResultSetHandler.class), anyVararg())).thenReturn(mockedWordResultList);

gives me a compilation error of:

java: reference to query is ambiguous   both method
<T>query(java.lang.String,java.lang.Object,org.apache.commons.dbutils.ResultSetHandler<T>)
in org.apache.commons.dbutils.QueryRunner and method
<T>query(java.lang.String,org.apache.commons.dbutils.ResultSetHandler<T>,java.lang.Object...)
in org.apache.commons.dbutils.QueryRunner match

This error happens in build 1.8.0-b128, but doesn't happen in 1.7.0_45. I'm using mockito 1.9.5.

What's the correct way to use anyVarArg() argument matching in java 8?

like image 448
Paul Sanwald Avatar asked Mar 24 '14 21:03

Paul Sanwald


Video Answer


1 Answers

The problem is that the type inference has been improved. anyVararg() is a generic method but you are using it in a nested method invocation. Before Java 8 the limitations of the type inference forced treating the method <T> T anyVararg() like <Object> Object anyVararg() when placed as an argument to another method invocation without inserting explicit type arguments.

So only query(String, ResultSetHandler, Object...) matched as the third argument was treated as being of type Object.

But now with Java 8 type inference works with nested method calls. Since for <T> T anyVararg() the type parameter <T> can be just anything, it can be ResultSetHandler as well. So query(String,Object,ResultSetHandler) also is a match candidate now.

(I omitted the type parameter <T> from the outer call in both cases to make it less confusing)

Since we have two possible matches now, the normal procedure of method selection applies here. And yes, it’s ambiguous. The first parameter is the same, String, but for the other two ResultSetHandler is more specific than Object but while one candidate accepts a more specific type for the second parameter, the other does for the third (and follow-ups).

It’s clear that type parameters that allow a method’s return type to be just anything are a source of ambiguity but APIs like Mockito’s containing such methods are a corner case of Java programming. You will have to force a type either the generic way Matchers.<Desired>anyVararg() or via type cast (Desired)anyVararg().

like image 175
Holger Avatar answered Sep 27 '22 22:09

Holger