在我的测试中,我有以下行:
when(client.runTask(anyString(), anyString(), isA(Iterable.class)).thenReturn(...)
isA(Iterable.class)
产生警告,它需要未经检查的转换才能符合Iterable<Integer>
。它的语法是什么?
isA(Iterable<Integer>.class)
isA((Iterable<Integer>)Iterable.class
不要工作。
有什么建议吗?
Mockito/Hamcrest和泛型类
是的,这是Mockito/Hamcrest的一个普遍问题。通常,将isA()
与泛型类一起使用会产生警告。
对于最常见的泛型类:anyList()、anyMap()
、anySet()
和anyCollection()
,有预先定义的Mockito匹配器。
建议:
Mockito 2.1.0中的anyIterable()
Mockito 2.1.0添加了一个新的anyIterable()方法来匹配Iterables:
when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)
在Eclipse中忽略
如果您只是想摆脱Eclipse中的警告。选项存在于Eclipse Indigo:之后
窗口>首选项>Java>编译器>错误/警告>通用types>忽略不可避免的泛型类型问题
使用@SuppressWarnings快速修复
如果你只有一次问题,我建议你这样做。我个人不记得曾经需要过isA(Iterable.class)
。
正如Daniel Pryden所说,可以将@SuppressWarnings
限制为局部变量或辅助方法。
使用具有TypeToken的通用isA()匹配器
这就永远解决了这个问题。但它有两个缺点:
- 语法不太漂亮,可能会让一些人感到困惑
- 您对提供
TypeToken
类的库有一个额外的依赖项。在这里,我使用了来自Guava的TypeToken类。Gson中还有一个TypeToken
类,JAX-RS中也有GenericType
类
使用通用匹配器:
import static com.arendvr.matchers.InstanceOfGeneric.isA;
import static org.mockito.ArgumentMatchers.argThat;
// ...
when(client.runTask(anyString(), anyString(), argThat(isA(new TypeToken<Iterable<Integer>>() {}))))
.thenReturn(...);
通用匹配器类:
package com.arendvr.matchers;
import com.google.common.reflect.TypeToken;
import org.mockito.ArgumentMatcher;
public class InstanceOfGeneric<T> implements ArgumentMatcher<T> {
private final TypeToken<T> typeToken;
private InstanceOfGeneric(TypeToken<T> typeToken) {
this.typeToken = typeToken;
}
public static <T> InstanceOfGeneric<T> isA(TypeToken<T> typeToken) {
return new InstanceOfGeneric<>(typeToken);
}
@Override
public boolean matches(Object item) {
return item != null && typeToken.getRawType().isAssignableFrom(item.getClass());
}
}
以下是我所做的:
// Cast from Class<Iterable> to Class<Iterable<Integer>> via the raw type.
// This is provably safe due to erasure, but will generate an unchecked warning
// nonetheless, which we suppress.
@SuppressWarnings("unchecked")
Class<Iterable<Integer>> klass
= (Class<Iterable<Integer>>) (Class) Iterable.class;
// later
isA(klass) // <- now this is typesafe
您可以在语句上方添加@SuppressWarnings("unchecked")
。没有其他方法,但如果它让您感到困扰,您可以将强制转换移动到辅助方法。
没有办法做到这一点。为了简化,您不能在没有警告的情况下初始化此变量:
Class<Iterable<Integer>> iterableIntegerClass = ?
一种解决方案可能是使用伪typedef反模式,,创建并使用IntegerIterable
接口
interface IntegerIterable extends Iterable<Integer> {}
然后
isA(IntegerIterable.class)
将不再产生警告。但是您必须扩展实现Iterable
的类,让它们实现IntegerIterable
:)例如:
public class IntegerArrayList extends ArrayList<Integer> implements IntegerIterable {}
嗯,好吃。。。
所以,我建议你考虑在你的方法中添加来掩盖裂缝
@SuppressWarnings("unchecked")