对于泛型方法的空参数,编译器的行为有所不同 [英] Compilers behave differently with a null parameter of a generic method

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

问题描述

下面的代码用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,然后抱怨 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 不符合类型边界变量 (s) 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

找到: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,即使它没有't 满足边界(因此会触发编译错误).

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):

这个程序不能编译:

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

状态:关闭,不是缺陷.

评估:这不是错误.推理算法无法从参数 (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.

JLS15.12.2.8 推断未解析的类型参数

然后将尚未推断的任何剩余类型变量推断为具有类型 Object

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:

Object &可比性>T max(Collection)

<小时>

进一步探索

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


Further explorations

Using explicit type parameters "fixes" the problem:

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

为了表明这与 null 参数关系不大,而与类型推断信息的绝对缺乏有关,您可以尝试例如以下任一声明:

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 方面存在错误.众所周知,不同的编译器之间存在分歧.

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.

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

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