为什么私有内部类对静态导入不可见?

时间:2016-03-17 09:12:58

标签: java visibility

我定义了一个私有内部枚举类,并尝试静态导入其中一个枚举值,如下所示:

public class OuterClass {

    private enum InnerEnum {
        ONE,
        TWO
    }

    public static void main(String... args) {
        System.out.print(ONE);
    }
}

这是不可能的,因为下面显示的静态导入语句不可见:

import static OuterClass.InnerEnum.ONE;

我必须将可见性从private扩展到package private才能使其正常工作。我非常清楚private的语义,但我的观点是为什么相同的代码,一旦写成完全限定的枚举值,就像这样:

System.out.print(InnerEnum.ONE);

有效但写得像这样:

import static OuterClass.InnerEnum.ONE;

...

System.out.print(ONE);

不是。 Java import语句(静态或非静态)仅引入别名。但对于私有类型,我们不允许引入别名。这对我来说很奇怪。

是否有人知道此限制背后的语言设计决定?

在我的情况下允许静态导入会发生哪些风险或危险?

我对出于技术动机的原因不感兴趣。

4 个答案:

答案 0 :(得分:1)

无法简单地允许import语句静态导入内部符号的一个原因是,您可以在同一Java源文件中包含多个顶级符号。

单一静态导入声明的含义在JLS §7.5.3

中给出
  

单静态导入声明从类型导入具有给定简单名称的所有可访问静态成员。这使得这些静态成员在单个名称下可用于编译单元的类和接口声明中,其中出现单静态导入声明

(强调我的)。现在考虑以下代码,它位于单个编译单元中,例如OuterClass.java

package mypackage;

import static mypackage.OuterClass.InnerEnum.ONE;

class OuterClass {
  private enum InnerEnum { ONE }
}

class OtherClass {
  void run() { System.out.println(ONE); }
}

现在:上面的引用说静态导入使整个编译单元中的ONE符号可用 - 其中包括OtherClass - 但InnerEnum应该在OtherClass中无法使用,因为OuterClass是私有的。

基本上,如果由于可见性原因而不允许您将该语句写为System.out.println(OuterClass.InnerEnum.ONE);,则您不应该通过静态导入等其他机制来规避该语句。

因此,语言设计应该(并且)阻止静态导入私有符号。 (事实上​​,这不仅限于静态导入:您也可以import mypackage.OuterClass.InnerEnum,原因完全相同。)

答案 1 :(得分:1)

阅读JLS第6.6章,其中涉及访问控制(https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6) 关于这个问题没有直接部分。

但我觉得作家们认为你不需要导入私人成员就太明显了。

稍后,他们也将此行为转移到静态导入。

那就是它。

答案 2 :(得分:0)

你可以像这样访问它:

protected void button(object sender, EventArgs e)
    {
int number=Convert.ToInt32(ViewState["hereYourThingToIncrement"]);
        ViewState["hereYourThingToIncrement"] = number+ 1;
        yourtxt.Text = ViewState["hereYourThingToIncrement"].ToString();

}

但由于它是私密的,你无法从public class OuterClass { private enum InnerEnum { ONE, TWO } public static void main(String[] args) { System.out.print(InnerEnum.ONE); } }

获得它

答案 3 :(得分:-1)

在我看来,它是JLS如何指定静态导入的错误概念。我会尝试解释我的意见:

  1. 概念 Java 导入或静态导入会引入一个简单名称作为完全限定名称的别名。导入不会为 Java语言添加任何功能,但可能仅提高源代码的可读性。如果你从 Java 中删除了导入,那么除了可读性之外你什么也没有。因此,对于 Java语言强制执行的所有访问和可见性规则,使用简单名称的源代码和使用完全限定名称的源代码之间应该没有区别。如果您的源代码在没有导入的情况下进行编译,并且您稍后通过添加导入来替换具有简单名称的完全限定名称,则代码应该(在我看来必须)按照之前的方式进行编译。

  2. 如果 Java编译器接受一个根本不定义导入的文件,而是在任何地方使用完全限定类型,这意味着所有访问规则都得到满足。

  3. Java编译器将分两个阶段编译文件,如下所述,可以静态导入具有私有访问范围的实体:< / p>

    1. 将所有简单名称替换为完全限定名称
    2. 编译文件。
  4. Java编译器实现中的缺陷是它确实应用了忽略使用范围的访问规则。通过在评估import语句时强制执行publicpackage private可见性而不检查引入的简单名称(即别名)的使用范围,它过于严格。

    更复杂的 Java编译器实现将能够允许私有实体的静态导入,这不会给代码带来任何缺陷或缺点。 Java编译器的工作方式现在强制执行不太可读的代码(在某些情况下)或者将私有实体的可见性扩展到私有包。

    此外, JLS (在我看来)应该尽可能不受技术问题的影响,引入了一条对语言设计无动机的规则。如上文 1。所述,无需强制执行或假设可见性不符合使用范围。

    我想在引入static imports之前,没有有效的私有类型导入方案。因此,可以在解析所有类型为public的导入或与定义导入的类型相同的包中时进行假设。我不知道为什么编译器确实检查了导入的可见性,因为在解析使用类型的标记时检查它应该是足够的,并且无论如何都应该发生,因为源代码的存在具有完全限定类型。

    引入static imports后,假设变得不真实,但编译器仍然存在。而不是纠正编译器以解决私有实体有效的新引入的场景,我们坚持旧的行为。仅使用完全限定类型和实体进行编译的代码,在静态导入在使用它们的范围内可见的私有实体后,可能不会。在我看来,这种行为是不一致的,可能会使源代码或类设计变得更糟。