以下代码与Eclipse完美编译,但无法使用javac进行编译:
public class HowBizarre {
public static <P extends Number, T extends P> void doIt(P value) {
}
public static void main(String[] args) {
doIt(null);
}
}
我简化了代码,所以现在根本不使用T.不过,我没有看到错误的原因。 出于某种原因,javac决定T代表Object,然后抱怨Object不符合T的界限(这是真的):
HowBizarre.java:6:不兼容的类型;推断类型参数 java.lang.Number,java.lang.Object不符合类型的边界 变量P,T
发现:
<P,T>
无效required:void
doIt(null); ^
请注意,如果我将null参数替换为非null值,则编译正常。
哪个编译器行为正确?为什么?这是其中之一的错误吗?
答案 0 :(得分:18)
问题是由于JLS规范要求否则必须将不可传递的类型参数推断为Object
,即使它不满足边界(并因此会触发编译错误)。
以下是“错误”报告的摘录(为了清晰起见,已进一步注释):
"Bug" ID 6299211 - method type variable: inference broken for null
此程序无法编译:
public class Try { void m() { java.util.Collections.max(null); } }
状态:已关闭,而非缺陷。
评估:这不是一个错误。推理算法无法从参数(
null
)收集任何信息,并且在对返回值有任何期望的地方不调用该方法。在这种情况下,编译器必须推断java.lang.Object
类型变量。
JLS 15.12.2.8 Inferring Unresolved Type Arguments
然后推断任何尚未推断的剩余类型变量具有类型
Object
但是,
Object
不是Comparable<? super Object>
的子类型,因此不在Collections.max
声明中的类型变量的范围内:
<T extends
Object & Comparable<? super T>
> T max(Collection<? extends T>)
使用显式类型参数“修复”问题:
HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac
为了表明这与null
参数关系不大,而且与绝对缺乏类型推理的信息有关,你可以尝试例如以下任一声明:
<T,U extends Comparable<T>> void doIt()
<T extends Number,U extends T> void doIt()
在任何一种情况下,调用doIt();
都不会在javac
中编译,因为它必须根据15.12.2.8将U
推断为Object
,即使这样做所以会触发编译错误。
虽然上面没有任何代码片段在javac
的某个版本中编译,但它们都在某个版本的Eclipse中完成。这将暗示Eclipse的一个错误。众所周知,不同的编译器之间存在分歧。
答案 1 :(得分:4)
这是javac中的一个错误。 Eclipse 推断正确的类型。
您可以致电doIt((Number) null);
即使您不打算使用javac进行开发,也要解决此问题,因为像ant或maven这样的工具会使用它,如果您在某些时候引入它们会导致问题。
答案 2 :(得分:2)