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不编译此示例。 问题是 – 为什么?
解释很简单,虽然它不会改变行为非常不直观的事实:
在解析要调用的方法时,编译器所做的第一件事就是找到具有正确名称方法的最小封闭范围。 只有这样才会出现重载解析和游戏中的其他内容。
现在发生的事情是包含toString()
方法的最小封闭范围是从Ainheritance它的A类。 因此,我们停在那里,不要再搜索。 遗憾的是,接下来编译器会尝试找到给定范围内方法的最佳拟合,并注意到它不能调用任何方法并给出错误。
这意味着永远不会静态导入名称与Object中的方法相同的方法,因为自然在范围内的方法优先于静态导入 (JLS详细描述了方法阴影,但对于这个问题,我认为它更简单记得那个)。
编辑: @alf提交了JLS的正确部分,该部分描述了那些想要全局的人的方法调用 。 它相当复杂,但问题并不简单,所以这是预期的。
它不是一个覆盖。 如果它确实有效, this.toString()
仍将访问A
的方法而不是Arrays.toString
,如果发生覆盖的话。
语言规范解释了静态导入仅影响static
方法和类型的解析:
导入名为n的字段的软件包p的编译单元c中的单静态导入声明d会影响整个c中由c中的静态导入按需声明导入的名为n的任何静态字段的声明。
在包p的编译单元c中的单静态导入声明d,它导入名为n的方法,其中包含签名s,它隐藏了任何名为n的静态方法的声明,其中签名s由c中的static-import-on-demand声明导入,整个c。
在包p的编译单元c中导入名为n的类型的单静态导入声明d会影响以下声明:
- 由c中的static-import-on-demand声明导入的任何名为n的静态类型 。
- 名为n的任何顶级类型 (第7.6节)在p的另一个编译单元(第7.3节)中声明。
- 由c中的type-import-on-demand声明(第7.5.2节)导入的任何名为n的类型。 整个c。
静态导入不会影响非静态方法或内部类型。
所以toString
不会影响非静态方法。 由于名称toString
可以引用A
的非静态方法,因此它不能引用Arrays
的static
方法,因此toString
绑定到范围内可用的名为toString
的唯一方法,即String toString()
。 该方法不能接受任何参数,因此您会收到编译错误。
第15.12.1节解释了方法解析,必须完全重写,以允许在static
方法中隐藏不可用的方法名称,但不允许在member
方法内部。
我的猜测是语言设计者希望保持方法解析规则简单,这意味着无论是否出现在static
方法中,相同的名称都意味着相同的东西,唯一可以改变的是可用的。
如果您尝试使用类似的代码,那么您将不会遇到任何编译器错误
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[])
方法,因此编译器正确地将它与静态导入匹配。
我不认为这是一个错误或与正常导入不同的东西。 例如,在正常导入的情况下,如果您有一个与导入的名称相同的私有类,则不会反映导入的私有类。