编译器的行为方式与generics方法的null参数不同
以下代码与Eclipse完美编译,但无法使用javac进行编译:
public class HowBizarre { public static 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)
发现:
无效
要求:无效
doIt(null); ^
请注意,如果我将null参数替换为非null值,则编译正常。
哪个编译器行为正确,为什么? 这是其中之一的错误吗?
问题是由于JLS规范要求必须将不可传递的类型参数推断为Object
,即使它不满足边界(并因此会触发编译错误)。
以下是“bug”报告的摘录(为了清楚起见,已进一步注释):
“Bug”ID 6299211 – 方法类型变量:推断为null
这个程序不编译:
public class Try { void m() { java.util.Collections.max(null); } }
州 : 关闭,而不是缺陷。
评价 : 这不是一个BUG 。 推理算法无法从参数中收集任何信息(
null
),并且在对返回值有任何期望的地方不调用该方法。 在这种情况下,编译器必须为类型变量推断java.lang.Object
。
JLS 15.12.2.8推断未解析的类型参数
然后推断任何尚未推断的剩余类型变量具有
Object
类型
但是,
Object
不是Comparable super Object>
的子类型Comparable super Object>
Comparable super Object>
因此不在Collections.max
声明中的type变量的范围内:
Object & Comparable super T>
Object & Comparable super T>
> T max(Collection extends T>)
进一步的探索
使用显式类型参数“修复”问题:
HowBizarre.doIt(null); // compiles fine in javac
为了certificate这与null
参数关系较少,而与类型推理的绝对缺乏信息有关,您可以尝试以下任一声明:
> void doIt() void doIt()
在任何一种情况下,调用doIt();
不能在javac
中javac
,因为它必须按照15.12.2.8推断U
为Object
,即使这样做会触发编译错误。
关于Eclipse的注释
虽然上面没有任何代码片段在某些版本的javac
,但它们都在某些版本的Eclipse中完成。 这将暗示Eclipse的一个错误。 众所周知,不同的编译器之间存在分歧。
相关问题
- generics在Eclipse中编译和运行,但不在javac中编译
这是javac中的一个错误。 Eclipse 推断出正确的类型。
你可以通过调用doIt((Number) null);
来解决它doIt((Number) null);
即使您不打算使用javac进行开发,也要解决此问题,因为像ant或maven这样的工具会使用它,如果您在某些时候引入它们会导致问题。
来自polygenelubricants的研究,sun的javac显然忠实于规范。 在过去我也使用过其他编译器,当发生冲突时,总会发现sun的javac是正确的。 Sun的优势在于将实施过程中的经验记录到规范中,而其他人则必须从头开始阅读规范 – 阅读时很难不入睡。