11

为什么代码替代(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

4 回答 4

4

我在 JLS 中找不到任何东西,@SuppressWarningsJLS 9.6.3.5)和未经检查的警告(JLS 5.1.9)部分似乎没有任何可能导致此问题的问题。我的猜测(没有自己测试您的 SSCE)是您在编译器中发现了一个错误。我建议向 Oracle 提交错误报告并将报告链接添加到您的问题中。

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

同时,您可以通过首先做您应该做的事情来消除所有问题,并动态生成空数组而不是强制转换现有数组,如本问题所述。

编辑

如果我的EMPTY_ARRAY那是一个static 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 及其可能调用的更多信息。

于 2012-12-11T22:46:33.000 回答
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)

这意味着OpenJDKOracle JDK都受到影响,JDK7JDK8都受到影响(是的,您已经可以下载它了)。

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

所以看起来这确实是一个javac错误。如果你报告它,请让我更新。

编辑:

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

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

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

于 2012-12-11T23:38:55.323 回答
1

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

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

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

这是错误 8016636(感谢提交),但一年多没有任何活动。

不幸的是,这在 Oracle 的错误跟踪器中并不少见。


额外的怪异:

  • 如果EMPTY_ARRAYis not则禁止警告final

  • 具有讽刺意味的是,如果您使用非数组(例如Object EMPTY_ARRAY = new Object(). (但不要那样做......)

于 2014-08-23T18:50:22.513 回答
1

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

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

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

正如布赖恩所建议的,这似乎是编译器中的一个错误。此外,此行为仅限于数组 - 将 EMPTY_ARRAY 替换为 anObject并将其强制转换为 aT不会按预期发出警告。

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)
于 2012-12-11T23:22:44.110 回答