为什么代码替代(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)
答案 0 :(得分:4)
我无法在JLS中找到任何内容,@SuppressWarnings
(JLS 9.6.3.5)和未经检查的警告(JLS 5.1.9)部分似乎没有任何可能导致的问题这个问题。我的猜测(没有自己测试你的SSCE)就是你在编译器中发现了一个错误。我建议filing a bug report with Oracle并在您的问题中添加报告链接。
简而言之,类中成员的顺序应完全独立于警告的处理方式。它可能只是未经检查的警告代码中的边缘情况,或者它可能是一个更大的问题。
与此同时,您可以通过执行您应该首先完成的操作来消除所有问题,并动态生成空数组而不是转换现有数组,如this question中所述。
修改强>
如果我的
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及其可能调用的更多信息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()
。 (但不要这样做......)