在具有泛型参数的泛型方法中使用 Spring RestTemplate

2022-08-31 13:10:37

要将泛型类型与Spring RestTemplate一起使用,我们需要使用(无法获取泛型响应实体<T>其中T是泛型类“SomeClass<SomeGenericType>ParameterizedTypeReference)

假设我有一些类

public class MyClass {
    int users[];

    public int[] getUsers() { return users; }
    public void setUsers(int[] users) {this.users = users;}
}

和一些包装类

public class ResponseWrapper <T> {
    T response;

    public T getResponse () { return response; }
    public void setResponse(T response) {this.response = response;}
}

所以,如果我试图做这样的事情,一切都很好。

public ResponseWrapper<MyClass> makeRequest(URI uri) {
    ResponseEntity<ResponseWrapper<MyClass>> response = template.exchange(
        uri,
        HttpMethod.POST,
        null,
        new ParameterizedTypeReference<ResponseWrapper<MyClass>>() {});
    return response;
}

但是,当我尝试创建上述方法的通用变体时...

public <T> ResponseWrapper<T> makeRequest(URI uri, Class<T> clazz) {
   ResponseEntity<ResponseWrapper<T>> response = template.exchange(
        uri,
        HttpMethod.POST,
        null,
        new ParameterizedTypeReference<ResponseWrapper<T>>() {});
    return response;
}

...并像这样调用此方法...

makeRequest(uri, MyClass.class)

...而不是得到对象,我得到的是对象。ResponseEntity<ResponseWrapper<MyClass>>ResponseEntity<ResponseWrapper<LinkedHashSet>>

我该如何解决这个问题?这是一个 RestTemplate 错误吗?

更新 1多亏了@Sotirios我理解了这个概念。不幸的是,我是新来的,所以我不能评论他的答案,所以写在这里。我不确定我是否清楚地理解了如何实现建议的方法来解决我的问题与key(由@Sotirios在他的答案末尾提出)。有人介意举个例子吗?MapClass


答案 1

不,这不是一个错误。这是黑客如何工作的结果。ParameterizedTypeReference

如果你看一下它的实现,它使用Class#getGenericSuperclass(),它声明

返回 Type,该类型表示由此类表示的实体(类、接口、基元类型或 void)的直接超类。

如果超类是参数化类型,则返回的 Type 对象必须准确地反映源代码中使用的实际类型参数。

因此,如果您使用

new ParameterizedTypeReference<ResponseWrapper<MyClass>>() {}

它将准确地返回 for 。TypeResponseWrapper<MyClass>

如果您使用

new ParameterizedTypeReference<ResponseWrapper<T>>() {}

它将准确地返回 for,因为这是它在源代码中的显示方式。TypeResponseWrapper<T>

当 Spring 看到 时,它实际上是一个对象,它不知道要使用的类型,因此它使用其默认值。TTypeVariable

你不能使用你提议的方式,使它在接受任何类型的意义上是通用的。请考虑编写一个键映射到该类的预定义密钥。ParameterizedTypeReferenceMapClassParameterizedTypeReference

您可以子类化并重写其方法以返回适当创建的 ,如 IonSpin 所建议的那样ParameterizedTypeReferencegetTypeParameterizedType


答案 2

我正在使用 org.springframework.core.ResolvableType for a ListResultEntity :

    ResolvableType resolvableType = ResolvableType.forClassWithGenerics(ListResultEntity.class, itemClass);
    ParameterizedTypeReference<ListResultEntity<T>> typeRef = ParameterizedTypeReference.forType(resolvableType.getType());

因此,在您的情况下:

public <T> ResponseWrapper<T> makeRequest(URI uri, Class<T> clazz) {
   ResponseEntity<ResponseWrapper<T>> response = template.exchange(
        uri,
        HttpMethod.POST,
        null,
        ParameterizedTypeReference.forType(ResolvableType.forClassWithGenerics(ResponseWrapper.class, clazz)));
    return response;
}

这只使用spring,当然需要一些关于返回类型的知识(但甚至应该适用于像Wrappr这样的东西>>只要你提供类作为varargs)


推荐