The following code compiles perfectly with Eclipse, but fails to compile with javac:
下面的代码使用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);
}
}
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):
我简化了代码,所以现在不使用T。尽管如此,我还是找不到这个错误的原因。出于某种原因,javac决定T代表Object,然后报怨对象不符合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
HowBizarre。java:6:不兼容的类型;推断类型参数(s)java.lang.Number . lang。对象不符合类型变量(s) P、T的边界。
found :
<P,T>
void发现:< P、T >空白
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?
哪些编译器行为正确,为什么?这是其中一个的错误吗?
3 个解决方案
#1
18
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).
这个问题是由JLS规范导致的,该规范规定,否则无法推断的类型参数必须被推断为对象,即使它不满足边界(因此会引发编译错误)。
The following is an excerpt from the "bug" report (which has been further annotated for clarity):
以下是“bug”报告的摘录(为了清晰起见,还作了进一步注释):
"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.
状态:关闭,而不是缺陷。
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 inferjava.lang.Object
for the type variable.评估:这不是一个错误。推理算法不能从参数(null)中收集任何信息,并且在返回值有任何期望的地方不会调用该方法。在这种情况下,编译器必须推断java.lang。对象,用于类型变量。
JLS 15.12.2.8 Inferring Unresolved Type Arguments
JLS 15.12.2.8推断未解析的类型参数
Any remaining type variables that have not yet been inferred are then inferred to have type
Object
任何尚未被推断的剩余类型变量都将被推断为具有类型对象
However,
Object
is not a subtype ofComparable<? super Object>
and thus not within the bounds of the type variable in the declaration ofCollections.max
:但是,Object不是可比 ,因此不在集合声明中类型变量的范围内。
<T extends
Object & Comparable<? super T>
> T max(Collection<? extends T>)
< T extendsObject &可比< ?超级T > > max(收集< ?扩展了T >)
Further explorations
Using explicit type parameters "fixes" the problem:
使用显式类型参数“修复”问题:
HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac
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()
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.
在任何一种情况下,调用doIt();不能在javac中编译,因为它必须根据15.12.2.8来推断您是对象,即使这样做会触发编译错误。
Note on 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.
虽然上面的代码片段没有一个是在某个版本的javac中编译的,但是它们都是在某个版本的Eclipse中编译的。这说明Eclipse有一个错误。众所周知,不同的编译器之间存在分歧。
Related questions
- Generics compiles and runs in Eclipse, but doesn’t compile in javac
- 泛型在Eclipse中编译和运行,但是在javac中没有编译。
#2
4
It's rather a bug in javac. Eclipse infers the correct type.
这是javac中的一个bug。Eclipse推断出正确的类型。
You can work it around by calling doIt((Number) null);
你可以通过调用doIt(数字)null来处理它;
Even if you don't plan to use javac for development, fix this issue, because tools like ant or maven use it and it will cause problems in case you introduce them at some point.
即使您不打算使用javac进行开发,也要解决这个问题,因为像ant或maven这样的工具会使用它,如果您在某个时候引入它们,它将导致问题。
#3
2
from polygenelubricants' research, sun's javac is apparently faithful to the spec. in the past I also used other compilers and when there's a conflict, it always turned out that sun's javac is correct. Sun has the advantage of documenting their experience from implementation into the spec, while the other guys have to read the spec from scratch - it is really hard not to fall in sleep when you read it.
从polygenelubricants的研究来看,sun的javac显然是忠实于此,过去我也使用过其他的编译器,当出现冲突时,结果总是证明sun的javac是正确的。Sun的优势在于将他们的经验从实现记录到规范中,而其他人则必须从头开始阅读规范——当你阅读它时,真的很难不睡着。
#1
18
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).
这个问题是由JLS规范导致的,该规范规定,否则无法推断的类型参数必须被推断为对象,即使它不满足边界(因此会引发编译错误)。
The following is an excerpt from the "bug" report (which has been further annotated for clarity):
以下是“bug”报告的摘录(为了清晰起见,还作了进一步注释):
"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.
状态:关闭,而不是缺陷。
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 inferjava.lang.Object
for the type variable.评估:这不是一个错误。推理算法不能从参数(null)中收集任何信息,并且在返回值有任何期望的地方不会调用该方法。在这种情况下,编译器必须推断java.lang。对象,用于类型变量。
JLS 15.12.2.8 Inferring Unresolved Type Arguments
JLS 15.12.2.8推断未解析的类型参数
Any remaining type variables that have not yet been inferred are then inferred to have type
Object
任何尚未被推断的剩余类型变量都将被推断为具有类型对象
However,
Object
is not a subtype ofComparable<? super Object>
and thus not within the bounds of the type variable in the declaration ofCollections.max
:但是,Object不是可比 ,因此不在集合声明中类型变量的范围内。
<T extends
Object & Comparable<? super T>
> T max(Collection<? extends T>)
< T extendsObject &可比< ?超级T > > max(收集< ?扩展了T >)
Further explorations
Using explicit type parameters "fixes" the problem:
使用显式类型参数“修复”问题:
HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac
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()
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.
在任何一种情况下,调用doIt();不能在javac中编译,因为它必须根据15.12.2.8来推断您是对象,即使这样做会触发编译错误。
Note on 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.
虽然上面的代码片段没有一个是在某个版本的javac中编译的,但是它们都是在某个版本的Eclipse中编译的。这说明Eclipse有一个错误。众所周知,不同的编译器之间存在分歧。
Related questions
- Generics compiles and runs in Eclipse, but doesn’t compile in javac
- 泛型在Eclipse中编译和运行,但是在javac中没有编译。
#2
4
It's rather a bug in javac. Eclipse infers the correct type.
这是javac中的一个bug。Eclipse推断出正确的类型。
You can work it around by calling doIt((Number) null);
你可以通过调用doIt(数字)null来处理它;
Even if you don't plan to use javac for development, fix this issue, because tools like ant or maven use it and it will cause problems in case you introduce them at some point.
即使您不打算使用javac进行开发,也要解决这个问题,因为像ant或maven这样的工具会使用它,如果您在某个时候引入它们,它将导致问题。
#3
2
from polygenelubricants' research, sun's javac is apparently faithful to the spec. in the past I also used other compilers and when there's a conflict, it always turned out that sun's javac is correct. Sun has the advantage of documenting their experience from implementation into the spec, while the other guys have to read the spec from scratch - it is really hard not to fall in sleep when you read it.
从polygenelubricants的研究来看,sun的javac显然是忠实于此,过去我也使用过其他的编译器,当出现冲突时,结果总是证明sun的javac是正确的。Sun的优势在于将他们的经验从实现记录到规范中,而其他人则必须从头开始阅读规范——当你阅读它时,真的很难不睡着。