32

在我的测试中,我有以下行:

when(client.runTask(anyString(), anyString(), isA(Iterable.class)).thenReturn(...)

isA(Iterable.class)产生警告,它需要未经检查的转换才能符合Iterable<Integer>. 那是什么语法?

isA(Iterable<Integer>.class)
isA((Iterable<Integer>)Iterable.class

不工作。

有什么建议么?

4

4 回答 4

31

Mockito/Hamcrest 和泛型类

是的,这是 Mockito/Hamcrest 的普遍问题。通常isA()与泛型类一起使用会产生警告。

最常见的泛型类有预定义的 Mockito 匹配器:anyList()anyMap()和。anySet()anyCollection()

建议:

Mockito 2.1.0 中的 anyIterable()

Mockito 2.1.0 添加了一个新的anyIterable()方法来匹配 Iterables:

when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)

在 Eclipse 中忽略

如果您只是想摆脱 Eclipse 中的警告。自Eclipse Indigo以来存在选项:

窗口 > 首选项 > Java > 编译器 > 错误/警告 > 泛型 > 忽略不可避免的泛型类型问题

使用@SuppressWarnings 快速修复

如果您只遇到一次问题,我建议您这样做。我个人不记得曾经需要一个isA(Iterable.class).

正如 Daniel Pryden 所说,您可以将其限制@SuppressWarnings为局部变量或辅助方法。

使用带有 TypeToken 的通用 isA() 匹配器

这很好地解决了这个问题。但它有两个缺点:

  • 语法不太漂亮,可能会使一些人感到困惑。
  • TypeToken您对提供该类的库有额外的依赖关系。在这里,我使用了Guava 的 TypeToken 类TypeTokenGson 和GenericTypeJAX-RS 中还有一个类。

使用通用匹配器:

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());
    }
}
于 2012-08-18T16:30:29.133 回答
8

这就是我所做的:

// 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
于 2012-08-17T21:07:33.910 回答
4

你可以在@SuppressWarnings("unchecked")上面添加语句。没有其他办法,但如果它打扰你,你可以将演员移动到辅助方法。

于 2012-08-17T19:55:39.563 回答
2

没有办法做到这一点。为简化起见,您不能在没有警告的情况下初始化此变量:

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")
于 2012-08-17T21:09:05.233 回答