为什么在 Java 7 中使用方法重载时自动装箱不会推翻可变参数?

发布于 2024-12-08 11:21:02 字数 1327 浏览 0 评论 0原文

我们的 Java 项目中有一个 LogManager 类,如下所示:

public class LogManager {

    public void log(Level logLevel, Object... args) {
        // do something
    }

    public void log(Level logLevel, int value, Object... args) {
        // do something else
    }
}

When compiling the project with OpenJDK 6 under Debian everyting 工作正常。使用 OpenJDK 7 时构建(使用 ant 完成) 产生以下错误并且构建失败:

[javac] /…/LogManager.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       log(logLevel, 1, logMessage);
[javac]       ^
[javac] /…/SomeOtherClass.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       logger.log(logLevel, 1, logMessage);
[javac]             ^

只要 1 没有自动装箱,方法调用应该是 明确的,因为 1 是一个 int 并且不能向上转型为 Object。那么为什么 自动装箱不会在这里否决可变参数吗?

Eclipse(使用 eclipse.org 中的 tar.gz 安装)编译它: 无论是否安装 OpenJDK 6。

非常感谢您的帮助!

编辑:

在这两种情况下,编译器都会获取选项source="1.6"target="1.6"。 Eclipse 编译注释只是作为注释。

We have a class LogManager in our Java project which looks like this:

public class LogManager {

    public void log(Level logLevel, Object... args) {
        // do something
    }

    public void log(Level logLevel, int value, Object... args) {
        // do something else
    }
}

When compiling the project with OpenJDK 6 under Debian everyting
works fine. When using OpenJDK 7 the build (done with ant)
produces the following errors and the build fails:

[javac] /…/LogManager.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       log(logLevel, 1, logMessage);
[javac]       ^
[javac] /…/SomeOtherClass.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       logger.log(logLevel, 1, logMessage);
[javac]             ^

As long as the 1 is not autoboxed, the method call should be
unambiguous as 1 is an int and cannot be upcast to Object. So why
doesn't autoboxing overrule varargs here?

Eclipse (installed using the tar.gz from eclipse.org) compiles it no
matter if OpenJDK 6 is installed or not.

Thank's a lot for your help!

Edit:

The compiler gets the option source="1.6" and target="1.6" in both cases. The Eclipse compiling note is just meant as a comment.

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

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

发布评论

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

评论(3

囚我心虐我身 2024-12-15 11:21:02

我猜这与错误 #6886431 有关,该错误似乎已在 OpenJDK 中修复7 也是如此。

问题是 JLS 15.12.2.5 选择最具体的方法
表示当一种方法的形式参数类型是后者的形式参数的子类型时,一种方法比另一种方法更具体。

由于 int 不是 Object 的子类型,因此您的方法都不是最具体的,因此您的调用是不明确的。

但是,以下解决方法是可能的,因为 IntegerObject 的子类型:

public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... } 

I guess it's related to bug #6886431, which seems to be fixed in OpenJDK 7 as well.

The problem is that JLS 15.12.2.5 Choosing the Most Specific Method
says that one method is more specific than another one when types of formal parameters of the former are subtypes of formal parameters of the latter.

Since int is not a subtype of Object, neither of your methods is the most specific, thus your invocation is ambiguous.

However, the following workaround is possible, because Integer is a subtype of Object:

public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... } 
月亮是我掰弯的 2024-12-15 11:21:02

Eclipse 使用它自己的编译器,因此 Eclipse 最终所做的事情遵循 SUN/Oracle 提供的编译器所做的事情;然而,有时(如本例)存在差异。

这可能是“两种情况”,并且可能在 Java 6 中,这个问题没有得到详细解决。由于 Java 强烈要求减少其环境中“模糊”含义的数量(以在许多平台上强制执行相同的行为),我想他们在 7 版本中加强了(或直接指定)已决定的行为。

您刚刚陷入了新规范澄清的“错误”方面。抱歉,但我认为您会将其中的一些内容写入

public void log(Level logLevel, Object... args) {
    if (args != null && args[0] instanceof Integer) {
      // do something else
    } else {
      // do something
    }
}

您的新解决方案中。

Eclipse uses it's own compiler, so what Eclipse does eventually follows what the SUN / Oracle provided compilers does; however, sometimes (like in this case) there are differences.

This could "go either way" and probably in Java 6, the issue wasn't addressed in detail. Since Java has a strong requirement to reduce the number of "ambiguous" meanings in its environment (to enforce same behavior across many platforms), I'd imagine that they tightened up (or directly specified) the decided behavior in the 7 release.

You just got caught on the "wrong" side of new specification clarification. Sorry, but I think you'll be writing a bit of this

public void log(Level logLevel, Object... args) {
    if (args != null && args[0] instanceof Integer) {
      // do something else
    } else {
      // do something
    }
}

into your new solution.

初吻给了烟 2024-12-15 11:21:02

这比谨慎的做法更接近边缘。除非你能在规范中找到关于行为的清晰语言,否则我会避免任何像这样的模棱两可的事情。

即使它规范中,代码的读者也不会通过语言律师来了解,因此您需要对其进行注释来进行解释,并且他们可能会也可能不会阅读注释。他们甚至可能不会考虑其中一种替代方案 - 只是看到一种适合的重载,然后运行它。一场事故等待发生。

This is skating way closer to the edge than is prudent. Unless you can find clear language in the spec as to behavior, I'd avoid anything ambiguous like this.

Even if it is in the spec, readers of your code won't have done the language lawyering to know, so you'll need to comment it to explain, and they may or may not read the comment. They may not even consider one of the alternatives - just see one overload that fits, and run with that. An accident waiting to happen.

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