[ACCEPTED]-Mockito isA(Class<T> clazz) How to resolve type safety?-java-5
Mockito/Hamcrest and generic classes
Yes, this is a general problem with Mockito/Hamcrest. Generally 16 using isA()
with generic classes produces a warning.
There 15 are predifined Mockito matchers for the 14 most common generic classes: anyList(), anyMap()
, anySet()
and anyCollection()
.
Suggestions:
anyIterable() in Mockito 2.1.0
Mockito 13 2.1.0 added a new anyIterable() method for matching Iterables:
when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)
Ignore in Eclipse
If 12 you just want to get rid of the warning 11 in Eclipse. Option exists since Eclipse Indigo:
Window 10 > Preferences > Java > Compiler 9 > Errors/Warnings > Generic types 8 > Ignore unavoidable generic type problems
Quick Fix with @SuppressWarnings
I 7 suggest you do this if you have the problem 6 only once. I personally don't remember ever 5 needing an isA(Iterable.class)
.
As Daniel Pryden says, you can 4 limit the @SuppressWarnings
to a local variable or a helper 3 method.
Use a generic isA() matcher with TypeToken
This solves the problem for good. But 2 it has two disadvantages:
- The syntax is not too pretty and might confuse some people.
- You have an additional dependency on the library providing the
TypeToken
class. Here I used the TypeToken class from Guava. There's also aTypeToken
class in Gson and aGenericType
in JAX-RS.
Using the generic 1 matcher:
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(...);
Generic matcher class:
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());
}
}
Here's what I do:
// 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
0
You can add @SuppressWarnings("unchecked")
above the statement. No other 2 way but if it bothers you, you can move 1 the cast to a helper method.
There is no way to do this. To simplify, you 9 can't initialize this variable without warning 8 :
Class<Iterable<Integer>> iterableIntegerClass = ?
One solution might be to use the pseudo-typedef antipattern,
,you 7 create and use an IntegerIterable
interface
interface IntegerIterable extends Iterable<Integer> {}
then
isA(IntegerIterable.class)
will no 6 more produce warning. But you will have 5 to extend the class implementing Iterable
to let 4 them implements IntegerIterable
:) For example :
public class IntegerArrayList extends ArrayList<Integer> implements IntegerIterable {}
Mmm tasty...
So, i 3 will sugest you to consider to just paper 2 over the cracks by adding to your method 1 :
@SuppressWarnings("unchecked")
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.