【问题标题】:Java static importsJava 静态导入
【发布时间】:2012-01-23 13:11:59
【问题描述】:

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

【问题讨论】:

  • 好像是指它的super的Object的toString()
  • 恕我直言,整个静态导入功能是一个糟糕的概念,会污染命名空间并破坏代码的可读性。不要偷懒,键入静态函数的封闭类/接口并不难,我们有 IDE。尽量避免使用静态导入。
  • @Jigar Joshi,它是如此接缝。但我找不到任何关于。而且,我们不能从静态上下文中调用Object.toString,所以我看不出这种行为有任何逻辑。
  • @buc Meh。静态导入在许多情况下都非常好,并且可以大大提高可读性。我无法想象在没有它们的情况下使用模拟库;那将是一场暴行。
  • @Dave 我同意,您只需要了解其中的陷阱 - 因为这些可能很糟糕。在这种情况下,它并没有那么糟糕,因为我们只是得到了一个编译错误,但在最坏的情况下,我们可能会调用错误的函数,这肯定会使调试变得有趣。

标签: java scope language-design overloading static-import


【解决方案1】:

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

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

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

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

编辑:@alf 请提交 JLS 的右侧部分,describes the method invocation 供那些想要全图的人使用。它相当复杂,但问题也不简单,所以这是意料之中的。

【讨论】:

【解决方案2】:

这不是覆盖。如果它确实有效,this.toString() 仍将访问 A 的方法,而不是 Arrays.toString,就像发生覆盖时的情况一样。

language specification 解释说静态导入只影响static 方法和类型的解析:

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

在包 p 的编译单元 c 中的单一静态导入声明 d 导入了一个名为 n 并带有签名 s 的方法,它隐藏了名为 n 且带有签名 s 的 任何静态方法 c 中的 static-import-on-demand 声明,贯穿整个 c。

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

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

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

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

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

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

【讨论】:

  • 除了方法bar是静态的,没有实例可以调用toString()
  • @StasKurilin par. 6.3.1 解释得更清楚。它表示实例方法Object.toString() 的声明会影响其范围内的任何其他方法。这包括Arrays.toString(Object[]) 的静态导入。这意味着,实际上,静态导入没有效果 - 编译器甚至不会考虑它来解析方法调用。
  • @inerdial,是的,但如果名称不是在 java.lang.Object 上定义的名称,那么它可能会产生影响。例如,import static Foo.compareTo; 可能在一个顶级类中有效,但在 implements Comparable 的另一类中无效。
【解决方案3】:

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

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[]) 方法,因此编译器正确地将其与 static import 匹配。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2013-01-02
      • 1970-01-01
      • 2014-06-14
      • 1970-01-01
      • 2018-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      相关资源
      最近更新 更多