【问题标题】:Java generics SuppressWarnings("unchecked") mysteryJava 泛型 SuppressWarnings("unchecked") 之谜
【发布时间】:2012-11-29 13:35:48
【问题描述】:

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

【问题讨论】:

  • 我可能是盲人,但区别在哪里?这两个代码示例在我看来都是一样的,只是顺序不同。
  • 我想他想知道为什么一个替代方案会产生警告而另一个不会?事实上,它们似乎是等价的。
  • Pro-SO 提示 1:与 cmets 中的人交谈时,使用 @ 符号。这会在该人的收件箱中放置一条通知,以便他们看到您的评论。 Pro-SO 提示 2:您可以在 @ 中键入他们姓名后的前几个字母,然后按 Tab 自动补全他们的姓名。
  • @Natix:很高兴我不是唯一一个被迷惑的人。 JDK 7 中似乎已经添加了它,突然出现了 6 中没有的警告。
  • 我确实接受了这个建议并向 Oracle 提交了一个错误。恐怕还有其他一些地方 JDK 1.7 也忽略了@SuppressWarnings("unchecked"),但我没有隔离它们。这一切都曾经在 1.6 中工作......

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


【解决方案1】:

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

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

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

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

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


额外的怪异:

  • 如果EMPTY_ARRAY 不是final,则禁止显示警告。

  • 具有讽刺意味的是,如果您使用非数组初始化警告,则会抑制警告,例如Object EMPTY_ARRAY = new Object()。 (但不要那样做……)

【讨论】:

  • 感谢您在错误跟踪器中找到错误。上次我看起来不能,而且我想我提交了不止一次。
【解决方案2】:

我可以在我的 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。

【讨论】:

  • 值得指出的是,这意味着问题出在 OpenJDK 和 Hotspot JDK 中。
【解决方案3】:

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

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

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

编辑

如果我的EMPTY_ARRAYstatic final,我看不出链接的提案将如何工作。

不要再使用static final,并在构造函数中提供Class&lt;T&gt;

@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

【讨论】:

  • 注意@SuppressWarnings,并比较 (1) 和 (2) 在 Natix 和 Pyranja 中指出的 cmets。
  • @JohannesErnst 是的,我在发帖后看到了。我正在更新。
  • Brian:我不喜欢复杂的变通方法使代码的可读性降低。但无论如何,如果我的 EMPTY_ARRAY 是静态决赛,我看不到链接提案将如何工作。 (Java 中特别令人气愤的部分是 EMPTY_ARRAY 确实并且总是为空,因此不存在任何类型安全问题。)
  • @JohannesErnst 请注意,即使使用空数组,强制转换也不一定安全。 String[] string = (String[]) new Object[0]; 这抛出:java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String
  • @Natix 完全正确。它适用于泛型的唯一原因是类型擦除。
【解决方案4】:

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

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 2017-10-04
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    相关资源
    最近更新 更多