Kre*_*ase 5 java generics mockito
我有一个ThingsProvider要尝试使用Mockito(简化版)测试的界面,定义如下:
interface ThingsProvider {
Iterable<? extends Thing> getThings()
}
Run Code Online (Sandbox Code Playgroud)
现在,当我要使用Mockito对其进行测试时,我正在执行以下操作(再次简化该问题):
ThingsProvider thingsProvider = mock(ThingsProvider.class);
List<Thing> things = Arrays.asList(mock(Thing.class));
when(thingsProvider.getThings()).thenReturn(things); // PROBLEM IS HERE
Run Code Online (Sandbox Code Playgroud)
编译错误信息: The method thenReturn(Iterable<capture#11-of ? extends Thing>) in the type OngoingStubbing<Iterable<capture#11-of ? extends Thing>> is not applicable for the arguments (List<Thing>)
现在,纯粹是为了进行测试,我将最后一行更改为
when(thingsProvider.getThings()).thenReturn((List)things); // HAHA take THAT generics!
Run Code Online (Sandbox Code Playgroud)
...但是在非测试代码中这样做显然是不好的。
我的问题:
Thing,这是接口所期望的。在#2上-我不简单返回的主要原因Iterable<Thing>是,有几种不同的扩展,其中具体类型中埋藏着返回特定子类型的东西,而我最终Type mismatch: cannot convert from Iterable<MagicalThing> to Iterable<Thing>遇到了类似的问题-也许解决方案是一种更好的解决方案解决这个问题?
对于那些不太熟悉Mockito的人,下面是更纯粹的Java版本#1:
public static void main(String...args) {
List<Integer> ints = Arrays.asList(1,2,3);
blah(ints);
Foo<Number> foo1 = new Foo<Number>();
foo1.bar(ints); // This works
Foo<? extends Number> foo2 = new Foo<Number>();
foo2.bar(ints); // NO COMPILEY!
}
private static void blah(List<? extends Number> numberList) {
// something
}
public static class Foo<T> {
public Object bar(List<? extends T> tList) {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所观察到的,返回类型中的通配符对于子类实现非常方便。
不过,这对该方法的调用者来说并没有多大区别;Iterable<Thing>如果您愿意,可以将其更改为;它在 javadoc 上更简单,但以牺牲子类实现者为代价。如有必要,子类可以进行暴力转换,例如List<MyThing> => Iterable<Thing>,通过擦除。
您的问题的原因是通配符捕获;基本上每个表达式在计算上下文表达式之前都会先进行通配符捕获。在方法调用中
foo( arg )
Run Code Online (Sandbox Code Playgroud)
arg在方法适用性/重载/推理完成之前,始终首先捕获通配符。在你的情况下,更通用的类型Iterable<? extends Thing>丢失了,变成了Iterable<CAP#>。
通常,通配符捕获不会造成任何问题;但 Mockito 语义并不常见。
第一个解决方案是避免类型推断;显式提供类型参数
Mockito.<Iterable<? extends Thing>>when(...
Run Code Online (Sandbox Code Playgroud)
或者正如 Delimanolis 建议的那样,使用目标类型来限制推理(在 java8+ 中)
OngoingStubbing<Iterable<? extends Thing>> stub = when(...
Run Code Online (Sandbox Code Playgroud)
看来 lambda 推断可能对这种情况有帮助
static <T> OngoingStubbing<T> whenX( Supplier<T> sup )
{
return Mockito.when( sup.get() );
}
whenX(thingsProvider::getThings).thenReturn(things);
// T = Iterable<? extends Thing>
Run Code Online (Sandbox Code Playgroud)
而且 - 如果你的界面足够简单,只需直接实现它而不是模拟它:)
List<Thing> things = ...;
ThingsProvider thingsProvider = ()->things;
Run Code Online (Sandbox Code Playgroud)