编译器的行为方式与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的子类型Comparable Comparable因此不在Collections.max声明中的type变量的范围内:

Object & Comparable Object & Comparable > T max(Collection)


进一步的探索

使用显式类型参数“修复”问题:

 HowBizarre.doIt(null); // compiles fine in javac 

为了certificate这与null参数关系较少,而与类型推理的绝对缺乏信息有关,您可以尝试以下任一声明:

 > void doIt()  void doIt() 

在任何一种情况下,调用doIt(); 不能在javacjavac ,因为它必须按照15.12.2.8推断UObject ,即使这样做会触发编译错误。


关于Eclipse的注释

虽然上面没有任何代码片段在某些版本的javac ,但它们都在某些版本的Eclipse中完成。 这将暗示Eclipse的一个错误。 众所周知,不同的编译器之间存在分歧。

相关问题

  • generics在Eclipse中编译和运行,但不在javac中编译

这是javac中的一个错误。 Eclipse 推断出正确的类型。

你可以通过调用doIt((Number) null);来解决它doIt((Number) null);

即使您不打算使用javac进行开发,也要解决此问题,因为像ant或maven这样的工具会使用它,如果您在某些时候引入它们会导致问题。

来自polygenelubricants的研究,sun的javac显然忠实于规范。 在过去我也使用过其他编译器,当发生冲突时,总会发现sun的javac是正确的。 Sun的优势在于将实施过程中的经验记录到规范中,而其他人则必须从头开始阅读规范 – 阅读时很难不入睡。