编译器对于泛型方法的空参数的行为有所不同

发布于 2024-09-05 04:47:11 字数 666 浏览 7 评论 0 原文

下面的代码用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);
      }
}

我简化了代码,所以现在根本不用T了。尽管如此,我还是没有看到该错误的原因。 由于某种原因 javac 认为 T 代表 Object,然后抱怨 Object 不符合 T 的边界(这是真的):

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

发现:无效

必填:无效

 doIt(null);
           ^

请注意,如果我用非空值替换空参数,它可以正常编译。

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

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);
      }
}

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: incompatible types; inferred type argument(s)
java.lang.Number,java.lang.Object do not conform to bounds of type
variable (s) P,T

found : <P,T>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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

咽泪装欢 2024-09-12 04:47:11

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

以下是“bug”报告的摘录(为了清晰起见,已对其进行了进一步注释):

“Bug”ID 6299211 - 方法类型变量:推断因 null 而中断

该程序无法编译:

公共类尝试{
    void m() {
        java.util.Collections.max(null);
    }
}

状态已关闭,不是缺陷。

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

<小时>

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

任何尚未推断的剩余类型变量都会被推断为 Object 类型

<小时>

但是,Object 不是 Comparable< 的子类型? super Object>,因此不在 Collections.max 声明中类型变量的范围内:

对象 &可比> T max(集合)



进一步探索

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

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

表明这与 null 参数关系不大,并且更多与绝对缺乏类型推断信息有关,您可以尝试以下任一声明:

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

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

在任何一种情况下,调用 doIt(); 都不会在 javac< 中编译/code>,因为它必须根据 15.12.2.8 推断 UObject,即使这样做会触发编译错误。


关于 Eclipse 的注意事项

虽然上面的代码片段都不能在某些版本的 javac 中编译,但它们都可以在某些版本的 Eclipse 中编译。这表明 Eclipse 方面存在错误。众所周知,不同的编译器之间存在分歧。

相关问题

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

"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 infer java.lang.Object for the type variable.


JLS 15.12.2.8 Inferring Unresolved Type Arguments

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


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 & Comparable<? 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

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.


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.

Related questions

嘴硬脾气大 2024-09-12 04:47:11

这是javac 中的一个错误。 Eclipse推断正确的类型。

你可以通过调用 doIt((Number) null); 来

解决这个问题。即使你不打算使用 javac 进行开发,也可以解决这个问题,因为像 ant 或 maven 这样的工具会使用它,并且它会如果您在某个时候引入它们,则会出现问题。

It's rather a bug in javac. Eclipse infers the correct type.

You can work it around by calling doIt((Number) 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.

荭秂 2024-09-12 04:47:11

根据 Polygenelubricants 的研究,sun 的 javac 显然符合规范。过去我也使用过其他编译器,当出现冲突时,结果总是sun的javac是正确的。 Sun 的优势在于将他们从实施过程中获得的经验记录到规范中,而其他人则必须从头开始阅读规范 - 当您阅读它时,真的很难不入睡。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文