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

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

问题描述

以下代码与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);
      }
}

我简化了代码,所有现在。仍然,我没有看到错误的原因。
由于某种原因,javac决定T代表Object,然后抱怨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

必填:void

       doIt(null);
           ^


请注意,如果我替换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);
    }
}

状态

State: CLOSED, NOT A DEFECT.

评估这不是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

但是, Object 不是的子类型Comparable< ;?超级对象> ,因此不在 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 可比较的super 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 / code>不会在 javac 中编译,因为它必须推断 U 为<$ c $

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天全站免登陆