请原谅错误的命名,下面描述的问题是对真实代码的非常粗略的简化和修改
我定义了以下类型:
interface Bundle {
// omitted
}
interface Content<T extends Bundle> {
void useBundle(T bundle);
Class<? extends T> getSupportedBundleClass();
// omitted
}
class Service<T extends Bundle> {
private final T bundle;
public Collection<Content<? super T>> someMethod(Collection<Object> allContents) {
// omitted
}
}
Service 中 someMethod 的目的是过滤 Content 的此类实例,其支持的 bundle 类与 包的类或更高层次的类。
例如:
鉴于Bundle的层次结构...
interface Bundle {}
interface A extends Bundle {}
class B implements A {}
...这个设置,...
Content<Bundle> contentBundle = // omitted
Content<A> contentA = // omitted
Content<B> contentB = // omitted
Service<Bundle> serviceBundle = // omitted
Service<A> serviceA = // omitted
Service<B> serviceB = // omitted
List<Object> allContents = List.of(contentBundle, contentA, contentB);
...以下说法正确的是:
serviceBundle.someMethod(allContents) => [contentBundle]
serviceA.someMethod(allContents) => [contentBundle, contentA]
serviceB.someMethod(allContents) => [contentBundle, contentA, contentB]
以下是 someMethod 的实现方式(使用辅助方法进行转换并解析通配符):
public Collection<Content<? super T>> someMethod(Collection<Object> allContents) {
return allContents.stream()
.map(obj -> obj instanceof Content ? (Content<? extends Bundle>) obj : null) // cast #1
.filter(Objects:nonNull)
.map(this::castOrNull)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private <U extends Bundle> Content<? super T> castOrNull(Content<U> content) {
return content.getSupportedBundleClass().isInstance(bundle) ? (Content<? super T>) content : null; // cast #2
}
我在castOrNull方法中的推理是,如果Service类中的bundle是支持的bundle类的实例 所提供的 Content 实例,则所提供的 Content 的类型参数是 T 的父类(super class)型(bundle 的类型)。
someMethod 正常工作,如预期的那样(如上面的示例所述)。但是,我收到未经检查的强制转换警告 Content<U>
至Content<? super T>
在带有注释 **//cast #2** 的行上(带有 **//cast #1** 的行就可以了)。有没有办法消除警告(当然,除了抑制它)?
最佳答案
这种未经检查的 Actor 阵容是不可避免的。您正在将不涉及类型变量的内容转换为涉及类型变量的内容。要检查此转换的有效性,JVM 需要知道类型变量 T
的类型。成立。不幸的是没有。无论您转换哪种中间类型,Object
到,您将引入类型变量 T
在某些时候,即运行时无法检查强制转换的有效性时,并且该强制转换将被标记为未经检查的强制转换 - 运行时不会对此强制转换执行任何操作,并且这可能实际上是无效的,一切都会在稍后爆炸。
当然,你可以引入自己的检查,比如
content.getSupportedBundleClass().isInstance(bundle)
但请注意,这与 JVM 本应执行的检查(用于检查 (Content<? super T>) content
的有效性)有很大不同。 ,类型为 T
在运行时可用。特别是,您的检查取决于 Content
的实现者实现getSupportedBundleClass
正确地,返回封闭类,而不是封闭类的某些子类:
class ContentBundle implements Content<Bundle> {
@Override
public void useBundle(Bundle bundle) {
}
@Override
public Class<? extends Bundle> getSupportedBundleClass() {
return B.class;
}
}
如果getSupportedBundleClass
,这不会是一个问题返回Class<T>
相反。
不知道字段如何bundle
被初始化,所以我不知道这是否会发生,但你的检查也取决于 Service.bundle
始终是 T
的实例,而不是 T
的子类。如果serviceBundle.bundle
实际上存储了 B
的一个实例,然后serviceBundle.someMethod(allContents)
将包含所有 3 个内容。
我建议您使用Class<T>
存储类型信息:
private final Class<T> bundleType;
public Service(Class<T> bundleType) {
this.bundleType = bundleType;
}
并使用isAssignableFrom
而不是isInstance
.
如果您认为自己的检查足够了(例如,如果您确定每个人都会以预期的方式实现 getSupportedBundleClass
),那么您正在执行检查类型转换的工作,并且你不应该担心 JVM 无法检查它。压制一下就好了。
在大多数情况下,JVM 不检查强制转换的解决方案就是您自己检查:)
关于Java 泛型 : getting rid unchecked cast warning (case described inside),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72058636/