26

只是通过实验,我发现即使在静态上下文中,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 不编译此示例。问题是——为什么?

4

4 回答 4

24

解释很简单,尽管它并没有改变行为非常不直观的事实:

在解析要调用的方法时,编译器首先要做的是找到具有正确名称的方法的最小封闭范围。只有这样才会有其他的东西,比如重载决议和游戏中的合作。

现在这里发生的是,包含toString()方法的最小封闭范围是类 A,它从Object. 因此,我们停在那里,不再寻找更远的地方。可悲的是,接下来编译器会尝试在给定范围内找到最适合的方法,并注意到它不能调用任何方法并给出错误。

这意味着永远不要静态导入名称与 Object 中的方法相同的方法,因为自然范围内的方法优先于静态导入(JLS 详细描述了方法阴影,但对于这个问题,我认为简单得多记住这一点)。

编辑:@alf请提交 JLS 的正确部分,该部分描述了那些想要全貌的人的方法调用。它相当复杂,但问题也不简单,所以这是意料之中的。

于 2012-01-23T13:34:38.920 回答
4

它不是一个覆盖。如果它确实有效,this.toString()仍然会访问方法,A而不是Arrays.toString像发生覆盖时那样。

语言规范解释说静态导入只影响static方法和类型的解析:

在包 p 的编译单元 c 中导入名为 n 的字段的单个静态导入声明 d 会隐藏由 c 中的按需静态导入声明导入的任何名为 n 的静态字段的声明,贯穿整个 c。

包 p 的编译单元 c 中的单个静态导入声明 d 导入了一个名为 n 并带有签名 s 的方法,它隐藏了任何名为 n 的带有签名 s 的静态方法的声明, 在整个 c.

在包 p 的编译单元 c 中导入名为 n 的类型的单静态导入声明 d 隐藏了以下声明:

  • 由 c 中的静态导入按需声明导入的任何名为 n 的静态类型。
  • 在 p 的另一个编译单元(第 7.3 节)中声明的任何名为 n 的顶级类型(第 7.6 节)。
  • 由 c 中的按需类型导入声明(第 7.5.2 节)导入的任何名为 n的类型。整个 c.

静态导入不会影响非静态方法或内部类型。

所以toString不会影响非静态方法。由于名称toString可以引用 的非静态方法A,因此它不能引用 的static方法,Arrays因此toString绑定到toString范围内唯一可用的命名方法,即String toString(). 该方法不能接受任何参数,因此会出现编译错误。

第 15.12.1 节解释了方法解析,并且必须完全重写以允许在static方法内而不是在方法内隐藏不可用的方法名称member

我的猜测是语言设计者希望保持方法解析规则简单,这意味着无论它是否出现在static方法中,相同的名称意味着相同的东西,唯一改变的是哪些是可用的。

于 2012-01-23T13:25:20.180 回答
1

如果您尝试遵循外观相似的代码,那么您将不会收到任何编译器错误

import static java.util.Arrays.sort;
public class StaticImport {
    public void bar(int... args) {
        sort(args); // will call Array.sort
    }
}

这个编译而你的不编译的原因是toString()(或在类 Object 中定义的任何其他方法)仍然适用于 Object 类,因为 Object 是你的类的父类。因此,当编译器从 Object 类中找到这些方法的匹配签名时,它会给出编译器错误。在我的示例中,由于 Object 类没有sort(int[])方法,因此编译器正确地将其与静态 import匹配。

于 2012-01-23T13:43:35.097 回答
0

我不认为这是一个错误或与正常导入不同的东西。例如,在正常导入的情况下,如果您有一个与导入的类同名的私有类,则不会反映导入的类。

于 2012-01-23T13:34:55.607 回答