【问题标题】:Why does the Eclipse Java compiler complain about an unchecked cast for inner derived types?为什么 Eclipse Java 编译器会抱怨内部派生类型的未经检查的强制转换?
【发布时间】:2016-07-28 16:38:08
【问题描述】:

考虑以下具有非静态内部类的泛型类Base<ID>

public class Base<ID> {
  ID id;

  public Base(ID id) {
    this.id = id;
  }

  public ID getId() {
    return id;
  }

  protected class BaseInner {
    String text = "Inner";
  }

  protected void method(BaseInner o) {
    o.text = "Foo";
  }
}

方法Base.method 采用BaseInner 类型的参数。现在考虑以下派生类。

public class Sub<ID> extends Base<ID> {
  public Sub(ID id) {
    super(id);
  }

  @Override
  protected void method(BaseInner o) {

    if (o instanceof Sub.SubInner) {
      SubInner sub = (SubInner) o; // Why does this cast emit an "unchecked cast" warning

      sub.text = "Bar";
      sub.value = 1337;
    }
  }

  protected class SubInner extends BaseInner {
    Number value = 42;
  }
}

Sub派生自Base,内部类SubInner派生自内部类BaseInnerSub的泛型类型参数ID作为类型参数传递给基类Base

我的问题:为什么编译器会抱怨 Sub 的覆盖 method 中从 BaseInnerSubInner 的转换?

为了理解警告,我尝试构建一个用例,其中 Sub&lt;A&gt;method 被一些 Sub&lt;B&gt;.SubInner 调用以证明警告是合理的。但是如果类型不兼容,我能想到的任何东西(包括? extends? super)都会在方法调用上发出编译器错误。

所以我想没有理由在method 中警告未经检查的演员表。我错过了什么吗?

由于方法覆盖发生在Sub&lt;ID&gt; 实现中,编译器能够将SubInner 的泛型类型扣除为Sub&lt;ID&gt;.SubInner。所以问题是,这不是重复的!

Eclipse 编译器警告是

类型安全:从 Base.BaseInner 到 Sub.SubInner 的未经检查的强制转换

如果我使用Base&lt;ID&gt;.BaseInner 作为方法参数,或者我在强制转换表达式中使用Sub&lt;ID&gt;.SubInner,警告不会改变。

【问题讨论】:

  • 只是一个猜测,但既然你在 if 语句中使用了o instanceof Sub.SubInner,你不应该在下面的语句中也使用Sub.SubInner吗?
  • 我已经尝试过了,并且还在演员阵容中使用了Sub&lt;ID&gt;.SubInner,但它总是会产生相同的警告。
  • 你用的是什么编译器? javac 8 没有任何警告。
  • 好的,我猜这是 Eclipse 编译器的问题!如果 Javac 完全不抱怨!我可以按预期投给SubInner! (没有像Sub&lt;ID&gt;.SubInnernecessary 这样的泛型类型的额外限定)
  • @Andy Tuner:这不是重复的。 javac 编译器不会为我的代码发出任何警告!请重新打开问题,以便我们添加正确答案。

标签: java generics compiler-warnings


【解决方案1】:

这似乎是 Eclipse 编译器问题。

Oracle 编译器 (javac) 不会在此处省略任何警告。

【讨论】:

    【解决方案2】:

    在这种情况下,未经检查的强制转换意味着您正在从非限定类型强制转换为泛型类型。

    Set<String> set = new HashSet();
    

    这一行也会产生未经检查的赋值警告。

    出现此警告是因为编译器无法推断强制转换在编译时是否安全。

    SubInner 实例具有对 Sub 实例的引用。您正在从 Base.BaseInner 进行投射,因此您也在从 Base 投射到 Sub。因此,您收到此警告。

    编辑

    解决方案是使用泛型参数。

    public class Base<ID> {
        ID id;
    
        public Base(ID id) {
            this.id = id;
        }
    
        public ID getId() {
            return id;
        }
    
        protected class BaseInner {
            String text = "Inner";
        }
    
        protected void method(Base<ID>.BaseInner o) {
            o.text = "Foo";
        }
    }
    
    
    public class Sub<ID> extends Base<ID> {
        public Sub(ID id) {
            super(id);
        }
    
        @Override
        protected void method(Base<ID>.BaseInner o) {
            SubInner sub = (SubInner) o; 
            sub.text = "Bar";
            sub.value = 1337;
        }
    
        protected class SubInner extends BaseInner {
            Number value = 42;
        }
    }
    

    【讨论】:

    • "此行也会产生未经检查的演员表警告。"不,它会产生原始类型警告。那里没有演员表。
    • 我已经尝试将泛型类型参数添加到 SubInner 和方法中,但没有成功。
    • 添加 SubInner 类型参数并不能解决问题,因为 SubInner 引用了一个通用的 Sub 对象。因此,您从中转换的基类也应该是通用的。否则,您将从非限定类型转换为泛型类型。
    猜你喜欢
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2012-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多