通过实验,我发现即使在静态上下文中,Java非静态方法也会覆盖范围内所有相同的命名方法。即使不允许参数重载。像
import java.util.Arrays;
import static java.util.Arrays.toString;
public class A {
public static void bar(Object... args) {
Arrays.toString(args);
toString(args); //toString() in java.lang.Object cannot be applied to (java.lang.Object[])
}
}
我在规范中找不到任何相关内容。这是一个错误吗?如果不是,有没有理由实施这样的语言?
UPD:Java 6不编译此示例。问题是 - 为什么?
答案 0 :(得分:24)
解释很简单,虽然它没有改变行为非常不直观的事实:
在解析要调用的方法时,编译器所做的第一件事就是找到具有正确名称方法的最小封闭范围。只有这样才会出现重载决议和游戏中的其他内容。
现在发生的事情是包含toString()
方法的最小封闭范围是A类,它从Object
继承它。因此,我们停在那里,不要再搜索。遗憾的是,接下来编译器会尝试找到给定范围内方法的最佳拟合,并注意到它不能调用任何方法并给出错误。
这意味着从不静态导入名称与Object中的方法相同的方法,因为自然在范围内的方法优先于静态导入(JLS)详细描述了方法阴影,但对于这个问题,我认为只记得这个问题要简单得多。
编辑:@alf为那些想要全貌的人提交了describes the method invocation的JLS的正确部分。它相当复杂,但问题并不简单,所以这是预期的。
答案 1 :(得分:4)
这不是一个覆盖。如果确实有效,this.toString()
仍会访问A
而不是Arrays.toString
的方法,如果覆盖发生的情况就是这样。
language specification解释静态导入仅影响static
方法和类型的解析:
在包p的编译单元c中导入名为n的字段的单静态导入声明d会影响由c中的静态导入按需声明导入的名为n的任何静态字段的声明,贯穿c
在包p的编译单元c中的单静态导入声明d,它导入一个名为n的方法,其中包含签名s,其中包含名为n的任何静态方法的声明,其中包含由签名导入的签名s c中贯穿c的静态导入按需声明。
在包p的编译单元c中导入名为n的类型的单静态导入声明d隐藏声明:
- 由c中的静态导入按需声明导入的任何静态类型命名n。
- 在p的另一个编译单元(第7.3节)中声明的名为n的任何顶级类型(第7.6节)
- 由c中的type-import-on-demand声明(第7.5.2节)导入的任何类型n 。 整个c。
静态导入不会影响非静态方法或内部类型。
因此toString
不会影响非静态方法。由于名称toString
可以引用A
的非静态方法,因此它不能引用static
的{{1}}方法,因此Arrays
绑定到唯一可在范围内使用的名为toString
的方法,即toString
。该方法不能接受任何参数,因此您会收到编译错误。
Section 15.12.1解释了方法解决方案,必须完全重写,以允许在String toString()
方法中隐藏不可用的方法名称,但不允许隐藏在static
方法内。
我的猜测是语言设计者希望保持方法解析规则简单,这意味着相同的名称意味着它是否出现在member
方法中是否相同,唯一改变的是可用。
答案 2 :(得分:1)
如果您尝试关注类似的代码,那么将不会获得任何编译器错误
import static java.util.Arrays.sort;
public class StaticImport {
public void bar(int... args) {
sort(args); // will call Array.sort
}
}
这个编译和你的原因不是因为Object是你的类的父类,toString()
(或类Object中定义的任何其他方法)仍然限定为Object类。因此,当编译器从Object类中找到这些方法的匹配签名时,它会给编译器错误。在我的例子中,因为Object类没有sort(int[])
方法,因此编译器正确地将它与静态导入匹配。
答案 3 :(得分:0)
我不认为这是一个错误或与正常导入不同的东西。例如,在正常导入的情况下,如果您有一个与导入名称相同的私有类,则不会反映导入的私有类。