安全投射 <?将 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
表示 x
是 y
,而 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
已按上述方式更改 - 因此以前安全的转换将不再安全。
在这种情况下,我个人觉得抑制警告没有问题。转换就是向编译器断言您拥有它没有的信息,这就是您所处的情况。
我有一个 HTTP 执行器 class:
Future<? extends Response> future = service.apply(request).toJavaFuture();
现在我想删除其中的 ? extends
部分,因为我不想让它对调用者来说太通用。基本上,我想 return Future<Response>
.
从我的角度来看:
x extends y
表示 x
是 y
,而 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
已按上述方式更改 - 因此以前安全的转换将不再安全。
在这种情况下,我个人觉得抑制警告没有问题。转换就是向编译器断言您拥有它没有的信息,这就是您所处的情况。