为什么这个代码在包含在泛型函数中时不起作用?

时间:2017-12-26 06:25:46

标签: java spring function generics jackson

我使用此代码执行HTTP POST请求并反序列化返回的值:

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();
}

然而,当代码放入函数时代码停止运行。它抛出java.lang.ClassCastException: java.base/java.util.LinkedHashMap cannot be cast to client.rest.MyClass。似乎某些类型信息在此过程中丢失了。

以下是2个测试用例的完整代码:

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. 为什么功能不起作用?
  2. 我怎样才能让它发挥作用?

2 个答案:

答案 0 :(得分:3)

回答1。

ParameterizedTypeReference<X> typeRef = new ParameterizedTypeReference<X>() {};

感谢最后的{}杰克逊能够使用反射找出运行时X的内容,但是在编译时解析X,所以如果你有MyClass }或T这正是它在运行时会得到的;它无法确定在运行时分配T的内容。

出于同样的原因,如果你继续使用无函数选项,但你最后删除{}它将编译,但它将导致相同的错误。

回答2。

而不是Class<T> returnType,你永远不会引用btw,你可以直接传递ParameterizedTypeReference<T> typeRef。然后,调用该帖子的代码需要在编译时确定T

@Test
    public void with_function() {
        ParameterizedTypeReference<MyClass> typeRef = new ParameterizedTypeReference<>() {}; 
        MyClass returnValue = post("some text", typeRef, "/test");
    }
}

但是我认为你应该考虑不依赖{}技巧的替代品,这可能会有问题。

您是否尝试过ParameterizedTypeReference的{​​{1}}?:

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(); } 的{​​{1}}非通用分配,例如T作为返回类型;它不适用于MyClass,因为它等同于返回MyClass.class。我想在这些情况下你需要构造并传递一个与更复杂的类型表达式相对应的不同ArrayList<MyClass> list; list.getClass()实例。

答案 1 :(得分:0)

您不需要ParameterizedTypeReference,因为您没有参数化类型。只需使用直接接受Class的{​​{3}}:

ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, returnType);