Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A strange generics edge case with Mockito.when() and generic type inference

I'm writing a test case that uses a java.beans.PropertyDescriptor using Mockito, and I want to mock the behavior of getPropertyType() to return an arbitrary Class<?> object (in my case, String.class). Normally, I would do that by just invoking:

// we already did an "import static org.mockito.Mockito.*"
when(mockDescriptor.getPropertyType()).thenReturn(String.class);

However, oddly, this does not compile:

cannot find symbol method thenReturn(java.lang.Class<java.lang.String>)

But when I specify the type parameter instead of depending on inference:

Mockito.<Class<?>>when(mockDescriptor.getPropertyType()).thenReturn(String.class);

everything is hunky dory. Why can't the compiler correctly infer the return type of when() in this case? I have never had to specify the parameter before like that.

like image 268
aarestad Avatar asked Jun 08 '12 16:06

aarestad


1 Answers

PropertyDescriptor#getPropertyType() returns an object of Class<?>, where the ? means "this is a type, but I don't know what it is". Let's call this type "X". So when(mockDescriptor.getPropertyType()) creates an OngoingStubbing<Class<X>>, whose method thenReturn(Class<X>) can only accept objects of Class<X>. But the compiler doesn't know what type this "X" is, so it will complain about you passing in a Class of any type. I think this is the same reason the compiler complains about calling add(...) on a Collection<?>.

When you explicitly specify Class<?> for the type on the when method, you're not saying that mockDescriptor.getPropertyType() returns a Class<?>, you're saying that when returns an OngoingStubbing<Class<?>>. Then, the compiler checks to make sure whatever you're passing into when is of a type that matches Class<?>; since getPropertyType() returns the "Class<X>" I mentioned earlier, it of course matches the Class<?> you specified.

So basically

// the inferred type is Class<"some type">
Mockito.when(mockDescriptor.getPropertyType())

// the specified type is Class<"any type">
Mockito.<Class<?>>when(mockDescriptor.getPropertyType())

In my IDE, the error message for your original code is

The method thenReturn(Class<capture#1-of ?>) in the type OngoingStubbing<Class<capture#1-of ?>> is not applicable for the arguments (Class<String>)

That capture#1-of ? is the "X" I described above.

like image 167
matts Avatar answered Nov 15 '22 21:11

matts