Java 静态导入

通过实验,我发现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 不编译此示例。问题是 - 为什么?


答案 1

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

解析要调用的方法时,编译器要做的第一件事就是找到具有正确名称的方法的最小封闭作用域。只有这样,才会出现其他事情,例如游戏中的过载分辨率和co。

现在这里发生的事情是,包含方法的最小封闭作用域是类 A,它从 中继承它。因此,我们止步于此,不再进一步搜索。可悲的是,接下来编译器试图在给定范围内找到最适合的方法,并注意到它不能调用其中任何一个并给出错误。toString()Object

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

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


答案 2

它不是覆盖。如果它确实有效,仍将访问的方法,而不是如果发生覆盖,则情况会是这样。this.toString()AArrays.toString

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

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

包 p 的编译单元 c 中的单静态导入声明 d 导入名为 n 的方法,该方法具有签名 s,该方法将隐藏任何名为 n 的静态方法的声明,该声明由 c 中的静态按需导入声明导入签名 s,贯穿整个 c。

包 p 的编译单元 c 中的单静态导入声明 d,该声明导入名为 n 的类型,该声明将隐藏以下各项的声明:

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

静态导入不会隐藏非静态方法或内部类型。

因此,不会隐藏非静态方法。由于该名称可以引用 的非静态方法 ,因此它不能引用 的方法,因此绑定到作用域中唯一可用的命名方法,即 。该方法不能接受任何参数,因此您会收到编译错误。toStringtoStringAstaticArraystoStringtoStringString toString()

第 15.12.1 节解释了方法解析,必须完全重写,以允许在方法内而不是方法内部重影不可用的方法名称。staticmember

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