当包装在通用函数中时,为什么此代码不起作用? [英] Why doesn't this code work when wrapped in a generic function?
问题描述
我使用以下代码执行HTTP POST请求并反序列化返回的值:
I use this code to perform a HTTP POST request and deserialize the returned value:
ParameterizedTypeReference<MyClass> typeRef = new ParameterizedTypeReference<>() {};
HttpEntity<Object> requestEntity = new HttpEntity<>("some text");
ResponseEntity<MyClass> result = restTemplate.exchange("/test", HttpMethod.POST, requestEntity, typeRef);
MyClass returnValue = result.getBody();
为了使其更易于使用,我尝试将代码包装在一个类似如下的函数中:
To make it easier to use, I tried to wrap the code in a function like so:
public <T> T post(Object content, Class<T> returnType, String url){
ParameterizedTypeReference<T> typeRef = new ParameterizedTypeReference<>() {};
HttpEntity<Object> requestEntity = new HttpEntity<>(content);
ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, typeRef);
return response.getBody();
}
但是,将代码放入函数中后,代码将停止运行.抛出java.lang.ClassCastException: java.base/java.util.LinkedHashMap cannot be cast to client.rest.MyClass
.好像某些类型信息在途中的某个地方丢失了.
However the code stops functioning when it's put in a function. It throws java.lang.ClassCastException: java.base/java.util.LinkedHashMap cannot be cast to client.rest.MyClass
. It seems as though some type information is lost somewhere along the way.
以下是2个测试用例的完整代码:
Here's the complete code in form of 2 test cases:
package client.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestGatewaySupport;
import static org.springframework.test.web.client.ExpectedCount.times;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
class MyClass {
public int getInt(){
return 1;
}
public void setInt(int i){}
}
public class TEMP {
public static RestTemplate restTemplate = new RestTemplate();
public static MockRestServiceServer mockServer;
@BeforeClass
public static void beforeClass() throws JsonProcessingException {
MyClass value = new MyClass();
// set up a mock server
RestGatewaySupport gateway = new RestGatewaySupport();
gateway.setRestTemplate(restTemplate);
mockServer = MockRestServiceServer.bindTo(gateway).build();
ObjectMapper objectmapper = new ObjectMapper();
String payload = objectmapper.writeValueAsString(value);
mockServer.expect(times(2), requestTo("/test"))
.andRespond(withSuccess(payload, MediaType.APPLICATION_JSON));
}
@Test
public void without_function() {
ParameterizedTypeReference<MyClass> typeRef = new ParameterizedTypeReference<>() {};
HttpEntity<Object> requestEntity = new HttpEntity<>("some text");
ResponseEntity<MyClass> result = restTemplate.exchange("/test", HttpMethod.POST, requestEntity, typeRef);
MyClass returnValue = result.getBody();
}
public <T> T post(Object content, Class<T> returnType, String url){
ParameterizedTypeReference<T> typeRef = new ParameterizedTypeReference<>() {};
HttpEntity<Object> requestEntity = new HttpEntity<>(content);
ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, typeRef);
return response.getBody();
}
@Test
public void with_function() {
MyClass returnValue = post("some text", MyClass.class, "/test");
}
}
我的问题有两个:
- 该功能为什么不起作用?
- 我如何使它工作?
推荐答案
答案为1.
ParameterizedTypeReference<X> typeRef = new ParameterizedTypeReference<X>() {};
感谢最终的{}
杰克逊能够使用反射找出运行时X
的内容,但是X
在编译时已解决,因此如果您使用的是MyClass
或T
它在运行时会得到什么;无法确定T
在运行时分配给了什么.
Thanks the final {}
jackson is able to find out the what X
is in run-time using reflection however X
is resolved in compilation time so if you have MyClass
or T
that is exactly what it will get in runtime; It won't be able to figure out what the T
is assigned to in runtime.
出于同样的原因,如果继续使用无功能选项,但最后删除{}
,它将编译但会导致相同的错误.
For the very same reason, if you keep using the function-less option but you remove the {}
at the end it will compile but it will result in the same error.
回答2.
您可以直接传递ParameterizedTypeReference<T> typeRef
,而不用引用Class<T> returnType
,而不必引用btw.然后,调用该帖子的代码将需要在编译时确定T
:
Instead of Class<T> returnType
, that you never make reference to btw, you could pass ParameterizedTypeReference<T> typeRef
directly. The code that call the post would then need to determine T
in compilation time:
@Test
public void with_function() {
ParameterizedTypeReference<MyClass> typeRef = new ParameterizedTypeReference<>() {};
MyClass returnValue = post("some text", typeRef, "/test");
}
}
但是,我认为您应该考虑不依赖{}
技巧的替代方法,这可能会引起问题.
However I think you should consider alternatives that do not rely on the {}
trick which might be problematic.
您尝试过ParameterizedTypeReference
的forType
吗?:
public <T> T post(Object content, Class<T> returnType, String url){
ParameterizedTypeReference<T> typeRef = ParameterizedTypeReference.forType(returnType);
HttpEntity<Object> requestEntity = new HttpEntity<>(content);
ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, typeRef);
return response.getBody();
}
在任何情况下,这都适用于像MyClass
这样的T
的非泛型分配,就像将MyClass.class
作为返回类型传递时一样;它不适用于ArrayList<MyClass> list; list.getClass()
,因为它等效于返回ArrayList.class
.我猜在这种情况下,您将需要构造并传递一个与更复杂的类型表达式相对应的Type
实例.
In any case this will work with non-generic assignations to T
like MyClass
as when passing MyClass.class
as the return type; It would not work with ArrayList<MyClass> list; list.getClass()
since it would be equivalent to return ArrayList.class
. I guess in those cases you would need to construct and pass a different Type
instance that would correspond to the more complex type expression.
这篇关于当包装在通用函数中时,为什么此代码不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!