Java泛型SuppressWarnings(“未选中”)之谜

时间:2012-12-11 22:43:49

标签: java generics java-7 suppress-warnings unchecked

为什么代码替代(1)编译时没有警告,代码替代(2)产生"未经检查的强制转换"警告?

两者都很常见:

class Foo<T> {
    Foo( T [] arg ) {
    }
}

替代方案(1):

class Bar<T> extends Foo<T> {
    protected static final Object [] EMPTY_ARRAY = {};

    @SuppressWarnings("unchecked")
    Bar() {
         super( (T []) EMPTY_ARRAY );
    }
}

替代方案(2):

class Bar<T> extends Foo<T> {
    @SuppressWarnings("unchecked")
    Bar() {
         super( (T []) EMPTY_ARRAY );
    }

    protected static final Object [] EMPTY_ARRAY = {};
}

替代(2)产生:

javac -Xlint:unchecked Foo.java Bar.java 
Bar.java:4: warning: [unchecked] unchecked cast
             super( (T []) EMPTY_ARRAY );
                           ^
  required: T[]
  found:    Object[]
  where T is a type-variable:
    T extends Object declared in class Bar
1 warning

这是:

java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)

4 个答案:

答案 0 :(得分:4)

我无法在JLS中找到任何内容,@SuppressWarningsJLS 9.6.3.5)和未经检查的警告(JLS 5.1.9)部分似乎没有任何可能导致的问题这个问题。我的猜测(没有自己测试你的SSCE)就是你在编译器中发现了一个错误。我建议filing a bug report with Oracle并在您的问题中添加报告链接。

简而言之,类中成员的顺序应完全独立于警告的处理方式。它可能只是未经检查的警告代码中的边缘情况,或者它可能是一个更大的问题。

与此同时,您可以通过执行您应该首先完成的操作来消除所有问题,并动态生成空数组而不是转换现有数组,如this question中所述。

修改

  

如果我的EMPTY_ARRAYstatic final,我看不到关联提案的效果。

不要再使它static final,并在构造函数中提供Class<T>

@SuppressWarnings("unchecked") // Still need this
public Bar(Class<T> clazz) {
    super((T[]) Array.newInstance(clazz, 0));
}

Java几乎从不使用final变量的值来表示警告,除非是死代码。否则,你会得到这样的边缘情况:

class Bar<T> extends Foo<T> {
    // Is it really empty?
    protected static final Object [] EMPTY_ARRAY = SomeOtherClass.getEmptyArray();

    @SuppressWarnings("unchecked")
    Bar() {
         super( (T []) EMPTY_ARRAY );
    }
}

他们必须将该逻辑写入编译器。对于像“空数组”这样的边缘情况来说,这是不必要的复杂化,而且,像这样的这种投射最终都是代码味道。

除了答案之外,您可能拥有的另一个选项是使用var args。 Foo

class Foo<T> {
    Foo( T ... arg ) {
    }
}

Bar

class Bar<T> extends Foo<T> {

    Bar() {
         super();
    }
}

这应该有效,并且它会消除所有强制转换,空数组,警告等。查看有关var args及其可能调用的更多信息here

答案 1 :(得分:3)

我可以在我的Windows 7 64b机器上模拟这种奇怪的行为:

  • Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
  • OpenJDK Runtime Environment (build 1.8.0-ea-lambda-nightly-h1669-20121030-b63-b00)

这意味着 OpenJDK Oracle JDK 都会受到影响, JDK7 JDK8 (是的,你已经可以下载了。)

Eclipse,因为它使用自己的JDT编译器,所以没有这个问题。

所以看来这确实是一个javac错误。如果您举报,请及时通知我。

编辑:

我还在我的计算机上找到了 JDK6 安装,所以我尝试了一个,实际上,它在两种情况下都没有警告,这是正确的行为

  • Java(TM) SE Runtime Environment (build 1.6.0_23-b05)

虽然我的Windows是64b,但所有JDK只有32b。

答案 2 :(得分:1)

我能够通过这种简化的设置重现行为:

class Bar<T> {
   @SuppressWarnings("unchecked")
   Bar() {
      T[]dummy = (T[]) EMPTY_ARRAY;
   }

   private static final Object [] EMPTY_ARRAY = {};
}

正如Brian所说,这似乎是编译器中的一个错误。此外,此行为仅限于阵列 - 将EMPTY_ARRAY替换为Object并将其强制转换为T不会按预期发出警告。

java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

答案 3 :(得分:1)

这是Oracle和OpenJDK 7和8中的一个错误。

你不能(不应该)通过重新排序类中的声明来获得编译警告/错误。

运行时错误,是的;编译错误,没有。

这是Bug 8016636(感谢您提交),但一年多来没有活动。

不幸的是,这在Oracle的bug追踪器中并不少见。


额外的怪异:

  • 如果EMPTY_ARRAY不是final,则会停止警告。

  • 具有讽刺意味的是,如果使用非数组初始化警告,则会禁止警告,例如: Object EMPTY_ARRAY = new Object()。 (但不要这样做......)