通用方法,等式约束

8

你好,我正在尝试将一个通用方法作为控制器基本方法实现,但我无法理解的问题出现在通用方法签名中。

<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, boolean isAdmin){

        String sender = isAdmin ? adminHash : userHash;
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", sender);
        headers.add("Content-Type", "application/json");
        HttpEntity<T> entity = new HttpEntity<>(body,headers);
        ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, body.getClass());
        return responseEntity;
}

我目前遇到的编译错误如下:

Incompatible equality constraint: T and capture of ? extends Object
2个回答

7
您没有明确指出问题发生的具体位置,但我认为这将在restTemplate.exchange(调用时发生,因为传递body.getClass()作为参数的结果。这是因为body.getClass()的返回类型是Class<? extends Object>,如Javadoc中所述:

实际结果类型为Class<? extends |X|>,其中|X|是调用getClass的表达式的静态类型的擦除

问题在于您无法保证body特定为T - 它可能是T的子类。因此,body.getClass()的结果可能不是Class<T>

如果您想要类型安全,您需要将其作为额外的参数传递给方法。

<T> ResponseEntity<T> makeApiCall(
    String path, HttpMethod httpMethod, T body, Class<T> bodyClass, 
    boolean isAdmin){
  // ...
  ResponseEntity<T> responseEntity =
      restTemplate.exchange(
          path, HttpMethod.POST, entity, bodyClass);
  // ...
}

请注意,获取 Class<T> 的唯一方法是使用类字面量,例如如果 TString,则使用 String.class。这排除了使用泛型体类型的可能性,因为没有泛型类字面量。

获取Class<T>的唯一方法是使用类字面量,例如如果T是String,则使用String.class。这排除了通用主体类型的使用,因为没有通用类字面量。这似乎有些自相矛盾。无论如何,在编译时编译器可以找出通用类型。那么将getClass()结果分配给Class<T>变量呢? - Lew Bloch
1
@LewBloch:我认为他的意思是T必须是可具体化的类型。否则,如果T是一个参数化类型,比如Foo<Bar>,那么你就必须提供一个Class<Foo<Bar>>,但这并不存在于安全范畴内。 - newacct

3
你需要将 body.getClass() 转换为 Class<T>
@SuppressWarnings("unchecked")
<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, boolean isAdmin){

    String sender = isAdmin ? adminHash : userHash;

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", sender);
    headers.add("Content-Type", "application/json");
    HttpEntity<T> entity = new HttpEntity<>(body,headers);
    ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, (Class<T>) body.getClass());
    return responseEntity;
}

为了实现类型安全,您需要显式地将类对象作为参数传递:
<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, Class<T> clazz, boolean isAdmin){
String sender = isAdmin ? adminHash : userHash;

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", sender);
    headers.add("Content-Type", "application/json");
    HttpEntity<T> entity = new HttpEntity<>(body,headers);
    ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, clazz);
    return responseEntity;
}

1
通用转换会引发警告,您应该处理它们。 - Lew Bloch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接