编译器的行为与通用方法的空参数不同 [英] Compilers behave differently with a null parameter of a generic method

查看:158
本文介绍了编译器的行为与通用方法的空参数不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码与Eclipse完美编译,但无法使用javac进行编译:

The following code compiles perfectly with Eclipse, but fails to compile with 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,然后抱怨该对象不符合T的边界(这是真的):

I simplified the code, so T is not used at all now. Still, I don't see a reason for the error. For some reason javac decides that T stands for Object, and then complains that Object does not conform to the bounds of T (which is true):


HowBizarre.java:6:不兼容的类型;推断类型参数
java.lang.Number,java.lang.Object不符合
变量的边界P,T

HowBizarre.java:6: incompatible types; inferred type argument(s) java.lang.Number,java.lang.Object do not conform to bounds of type variable (s) P,T

找到:< P,T> void

required:void

required: void

       doIt(null);
           ^


请注意,如果我替换空参数具有非空值,它编译得很好。

Note that if I replace the null parameter with a non-null value, it compiles fine.

哪个编译器的行为正确,为什么?这是他们中的一个的错误?

Which of the compilers behaves correctly and why? Is this a bug of one of them?

推荐答案

问题是由于JLS规范要求否则不能发送类型的参数必须被推断为 Object ,即使它不满足边界(并因此触发编译错误)。

The problem is due to a JLS specification that mandates that otherwise uninferrable type arguments must be inferred as Object, even if it doesn't satisfy the bounds (and would consequently trigger a compilation error).

以下是错误报告摘录(为了清楚起见,进一步注释):

The following is an excerpt from the "bug" report (which has been further annotated for clarity):


BugID 6299211 - 方法类型变量:针对空的推断推断



此程序不编译:

"Bug" ID 6299211 - method type variable: inference broken for null

This program does not compile:

public class Try {
    void m() {
        java.util.Collections.max(null);
    }
}

状态关闭,而不是缺陷。

评估这不是一个BUG 。推理算法无法从参数( null )收集任何信息,并且在返回值有任何期望的地方不调用该方法。在这种情况下,编译器必须为类型变量推断 java.lang.Object

Evaluation: THIS IS NOT A BUG. The inference algorithm cannot gather any information from the argument (null) and the method is not called in a place where there are any expectations on the returned value. In such cases the compiler must infer java.lang.Object for the type variable.

JLS 15.12.2.8推荐未解析的类型参数

然后推断任何尚未被推断的剩余类型变量要有类型对象

Any remaining type variables that have not yet been inferred are then inferred to have type Object

然而,对象不是的子类型可比较超级对象> ,因此不在声明中的类型变量的范围内 Collections.max

However, Object is not a subtype of Comparable<? super Object> and thus not within the bounds of the type variable in the declaration of Collections.max:

< T extends Object&可比< ;?超级T> > T max(Collection<?extends T>)






进一步的探索



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


Further explorations

Using explicit type parameters "fixes" the problem:

HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac

为了表明这与 null 参数更多与绝对缺乏类型推断的信息有关,您可以尝试eg以下声明之一:

To show that this has less to do with a null argument and more to do with the absolute lack of information for type inferrence, you can try e.g. either of the following declarations:

<T,U extends Comparable<T>> void doIt()

<T extends Number,U extends T> void doIt()

在任一情况下,调用 doIt(); 不会在 javac 中编译,因为它必须推断 U Object 根据15.12.2.8,即使这样做会触发编译错误。

In either case, an invocation doIt(); doesn't compile in javac, as it must infer U to be Object as per 15.12.2.8, even if doing so would trigger a compilation error.

上面的代码片段没有在某些版本的 javac 中编译,一些版本的Eclipse。这将提示Eclipse的一个bug。众所周知,不同编译器之间存在分歧。

While none of the snippets above compile in some version of javac, they all do in some version of Eclipse. This would suggest a bug on Eclipse's part. It's been known that there are disagreements between the different compilers.

  • Generics compiles and runs in Eclipse, but doesn’t compile in javac

这篇关于编译器的行为与通用方法的空参数不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆