安全投射 <?将 Response> 扩展到 <Response>

Safely casting <? extends Response> to <Response>

我有一个 HTTP 执行器 class:

Future<? extends Response> future = service.apply(request).toJavaFuture();

现在我想删除其中的 ? extends 部分,因为我不想让它对调用者来说太通用。基本上,我想 return Future<Response>.

从我的角度来看:

x extends y

表示 xy,而 y 不是 x

这意味着 x 可以转换为 y

在我看来,这可以安全地完成,因为 x 总是扩展 y

为什么下面的不安全?


    Future<? extends Response> future = service.apply(request).toJavaFuture();

    Future<Response> futureResponse = (Future<Response>) future;

一个Future<? extends Response>变量可以被赋值为Future<Response>Future<ResponseSubClass1>Future<ResponseSubClass2>

无法将 Future<ResponseSubClass1> 实例安全地分配给 Future<Response> 变量,因为这将允许将 ResponseSubClass2 分配给 Future<ResponseSubClass1>.

因此,您尝试的转换是不安全的。

我想说这是编译器对泛型类型参数的限制。编译器知道您正在参数化 class 但它没有关于如何使用它的任何上下文。

您的 Future 是数据持有者。您不能将任何内容放入其中,只能从中获取请求。在这种情况下,您期望 Future<? extends Response>Future<Response> 的行为相同,因此可以安全地进行转换,这是完全合理的。这两个东西都是请求的提供者。确切的实例可能是 subclasses,但是无论我们从 Future 中得到什么,都肯定会实现 Request 的方法,所以这个转换看起来应该是安全的。

当您的 class 不是纯粹的供应商,而是在使用数据时,问题就来了。假设我们在Future中添加了一个方法来设置值:

interface Future<T>
{
    void setValue(T value);
    //...
}

我们有一个 Future<? extends Response>,它被创建为 Future<ChildResponse>。当我们投射它 Future<Response> 时,我们现在可以用 Response 调用 setValue,而在投射之前我们需要一个 ChildResponse。这就是演员阵容不安全的原因。

实际上,编译器不够聪明,无法区分这两种情况,因此最好的决定是始终将转换声明为不安全的。即使编译器可以做出区分,但接口可能会更改这一事实使问题变得更加复杂 - 假设 Future 已按上述方式更改 - 因此以前安全的转换将不再安全。

在这种情况下,我个人觉得抑制警告没有问题。转换就是向编译器断言您拥有它没有的信息,这就是您所处的情况。