问题
This is the method:
protected <T> TestPageResult<T> getTestPageResutForRequest(MockHttpServletRequestBuilder request) throws Exception {
String responseJson = mockMvc.perform(request).andReturn().getResponse()
.getContentAsString();
TestPageResult<T> response = getObjectMapper().readValue(responseJson,
new TypeReference<TestPageResult<T>>() {
});
return response;
}
I call it like this:
TestPageResult<SomeDto> pageResult = this.<SomeDto>getTestPageResutForRequest(getRequest());
TestPageResult is:
protected static class TestPageResult<T> {
private List<T> items;
private long totalCount = -1;
public TestPageResult() {
}
//omitted getters and setters
}
The resulting pageResult.getItems() contains a List of LinkedHashMap instead of a list of SomeDto. If I were to just hardcode the SomeDto type in the objectMapper.readValue method I'd get the correct results.
What's the problem?
edit: The suggested duplicated did solve my problem - kind of. I used:
JavaType type = getObjectMapper().getTypeFactory().constructParametricType(TestPageResult.class, clazz);
TestPageResult<T> response = getObjectMapper().readValue(responseJson, type);
Problem is there is no going around not passing down a Class argument to the method. So the method looks ugly due to both passing a generic type and the same thing as a Class. Obviously you can just not pass the generic now but this way a casting would be required and adding SuppressWarnings and so on.
回答1:
The problem is erasure. All these <T>
parameters don't exist in the compiled code, after they're erased. This means that source new TypeReference<TestPageResult<T>>()
looks like new TypeReference<TestPageResult>()
once compiled, which is not what you want. (Similar to how a List<String>
ends up being a List
in compiled code, and it's just compile-time validation that you don't add Integer
s to your String
List
.)
I think there's roughly two ways to deal with this (in this case), both of these you already stumbled upon:
- Either you create a type that properly represents what you want, such as:
new TypeReference<TestPageResult<SomeDto>>()
, orclass SomeDtoPageResult extends TestPageResult<SomeDto>
which you can then use in places likereadValue(..., SomeDtoPageResult.class)
; - Or you create a complete class representation, like you were doing with
JavaType
What you really want won't work. Your best bet is to tinker and come up with the cleanest code that solves it. Generics let you express really elaborate structures, and when you serialize an actual instance (nested objects), that comes out just fine, but when the classes need to be introspected at runtime, e.g. for deserialization (your use case) or to build a model (e.g. to generate Swagger docs), this becomes problematic.
来源:https://stackoverflow.com/questions/48804508/objectmapper-using-typereference-not-working-when-passed-type-in-generic-method