为什么';在Java7中使用方法重载时,自动装箱是否会否决varargs?
我们的Java项目中有一个类LogManager,如下所示:为什么';在Java7中使用方法重载时,自动装箱是否会否决varargs?,java,compiler-construction,overloading,variadic-functions,autoboxing,Java,Compiler Construction,Overloading,Variadic Functions,Autoboxing,我们的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 } } 在Deb
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。那为什么呢
在这里,自动装箱不就否决了varargs吗
Eclipse(使用Eclipse.org中的tar.gz安装)编译它
不管是否安装了OpenJDK 6
非常感谢你的帮助
编辑:
编译器在这两种情况下都会获得选项
source=“1.6”
和target=“1.6”
。Eclipse编译说明只是一个注释。Eclipse使用自己的编译器,因此Eclipse最终会遵循SUN/Oracle提供的编译器的做法;然而,有时(像在本例中)会有差异
这可能是“两种方式中的一种”,可能在Java6中,这个问题没有得到详细的解决。由于Java强烈要求减少其环境中“模糊”含义的数量(在许多平台上强制执行相同的行为),我认为他们收紧了(或直接指定)7版本中的决定行为
您刚刚被发现在新规范澄清的“错误”方面。对不起,但我想你会写一点这个
public void log(Level logLevel, Object... args) {
if (args != null && args[0] instanceof Integer) {
// do something else
} else {
// do something
}
}
我想这与bug有关,OpenJDK 7中似乎也修复了这个bug 问题是 表示当前者的形式参数类型是后者的形式参数的子类型时,一种方法比另一种方法更具体 由于
int
不是Object
的子类型,因此您的两个方法都不是最具体的,因此您的调用是不明确的
但是,由于整数
是对象
的子类型,因此可以采用以下解决方法:
public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... }
这比谨慎的做法更接近边缘。除非你能在规范中找到关于行为的清晰语言,否则我会避免任何像这样模棱两可的事情
即使它在规范中,您代码的读者也不会通过语言律师了解,因此您需要对它进行注释以进行解释,他们可能会也可能不会阅读注释。他们甚至可能不考虑其中的一种选择——只看到一个适合的负载,然后运行。事故即将发生。使用您的解决方案可以编译项目,但会破坏方法覆盖的美观:(@Michael:如果声明一个调用
log(Level-Level,int-i)的方法log(Level,i,new-Object[]{}),您也可以尝试一下
有助于编译器-我手头没有Java 7环境可供尝试。(此外,方法重载并不漂亮,因为它会使方法解析变得不必要的复杂,从而导致这些边缘情况;至少与能够很好地覆盖您的用例的命名和可选参数相比。)这只会导致一个未使用的方法,因为我们永远不需要没有任何进一步参数的方法日志,即日志消息。显然,我的意思是方法重载而不是覆盖。6年前我不喜欢它,但现在我可以看到它的美。我不认为它不必要的复杂,因为它可以定义得非常清楚。从我的角度来看这是OpenJDK 7编译器的一个bug;)@Michael,不是编译器中的bug;但是,可能是语言中的一个bug。将内置类型升级为对象类型时,存在概念冲突。您是否尝试过日志(级别、整数、对象…
?这可能会对您有利地使用自动装箱,而不会很快与整数冲突。整数是一个对象
,它会导致与日志(级别,对象…
绑定。就我个人而言,我理解内置类型折衷,但我更喜欢常量都是对象的语言。为什么不intx=4;System.out.println(x.toString())
?int x不是常数。Java中没有真正的常量(只有final
类型)。将int
s作为非对象有其好处,因为它们更有效。允许像x.toString()
这样的操作将被限制在很少的函数中,对程序员的好处也不会太大。顺便说一句,我更喜欢函数式编程世界的语言:)我尝试使用Integer而不是int,它很有效(使用OpenJDK 6和7)。但是对于int和调用日志(logLevel,1,logMessage1),我的方法签名应该更具体,因为只需要上传,不需要自动装箱。所以我可以看出它是这样工作的,但我真的不明白为什么。我希望你的签名会出现错误,而不是我的签名。+1因为在我在评论中得出相同结论之前,我没有费心重读其他答案。还没有人在理解代码的这一部分时遇到问题。可能是因为这是一个只有硕士和博士才能使用的计算机科学椅子项目。学生:)但毕竟,我认为Java认为自动装箱+升级比只升级更昂贵。