Java 泛型 : getting rid unchecked cast warning (case described inside)

标签 java generics inheritance casting type-erasure

请原谅错误的命名,下面描述的问题是对真实代码的非常粗略的简化和修改

我定义了以下类型:

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
  }  
}

ServicesomeMethod 的目的是过滤 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/

相关文章:

java - 如何实现接口(interface)泛型方法

java - 扩展 ArrayList<Date> : How to use Date object in it?

inheritance - 受歧视的工会和继承

java - 为什么选择 Float 而不是 Double?反之亦然?

java - 为什么 Java 泛型可以转换不可分配的类型(Every Exception 到 RuntimeException)?

java - JTable - 删除行问题

ios - 如何在 Swift 中定义闭包数组?

c++ - 一个类可以同时继承抽象类和 CRTP 类吗?

java - 无法使用 Secret ARN 连接到 Aurora Serverless

java - 获取 BufferedReader 读取的文件中的偏移量?