我们的 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 everyting 下使用 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。 那么为什么 自动装箱不会在这里否决可变参数吗?
Eclipse(使用来自 eclipse.org 的 tar.gz 安装)编译它: 不管是否安装 OpenJDK 6。
非常感谢您的帮助!
编辑:
在这两种情况下,编译器都会获得选项
source="1.6"
和 target="1.6"
。 Eclipse 编译注释只是作为注释。
我猜这与 bug #6886431 有关,该 bug 似乎也在 OpenJDK 7 中得到了修复。
问题在于 JLS 15.12.2.5 选择最具体的方法 表示当一种方法的形式参数类型是后者的形式参数的子类型时,一种方法比另一种方法更具体。
由于
int
不是 Object
的子类型,因此您的方法都不是最具体的,因此您的调用是不明确的。
但是,以下解决方法是可能的,因为
Integer
是 Object
的子类型:
public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... }
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
}
}
进入您的新解决方案。
这比谨慎的做法更接近边缘。 除非您能在规范中找到关于行为的清晰语言,否则我会避免任何像这样的模棱两可的事情。
即使它是在规范中,你的代码的读者也不会通过语言律师来了解,所以你需要对其进行评论来解释,他们可能会也可能不会阅读评论。 他们甚至可能不会考虑其中一种替代方案 - 只是看到一种适合的重载,然后运行它。 一场事故即将发生。