对于泛型方法的空参数,编译器的行为有所不同 [英] Compilers behave differently with a null parameter of a generic method
问题描述
下面的代码用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.
然后将尚未推断的任何剩余类型变量推断为具有类型 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 extends T>)
<小时>
进一步探索
使用显式类型参数修复"了问题:
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屋!