为什么在 Java 7 中使用方法重载时,自动装箱不推翻 varargs?

我们在 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
    }
}

在 Debian 下使用 OpenJDK 6 编译项目时,一切工作正常。使用 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。那么,为什么自动装箱在这里不推翻 varargs 呢?

Eclipse(使用 eclipse.org 的tar.gz安装)无论是否安装了OpenJDK 6,都会编译它。

非常感谢您的帮助!

编辑:

编译器获取该选项,在这两种情况下。Eclipse 编译注释只是一个注释。source="1.6"target="1.6"


答案 1

我想它与错误#6886431有关,这似乎也在OpenJDK 7中修复。

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

由于 不是 的子类型,因此您的两种方法都不是最具体的,因此您的调用是模棱两可的。intObject

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

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

答案 2

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

到您的新解决方案中。