编译器使用泛型方法的null参数表现不同

时间:2010-06-08 18:31:33

标签: java eclipse generics null compilation

以下代码与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值,则编译正常。

哪个编译器行为正确?为什么?这是其中之一的错误吗?

3 个答案:

答案 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 extendsObject & 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,即使这样做所以会触发编译错误。


关于Eclipse的注释

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

相关问题

答案 1 :(得分:4)

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

您可以致电doIt((Number) null);

来解决问题

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

答案 2 :(得分:2)

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