【问题标题】:Accessing Static Final Fields in Inner Classes in Java访问 Java 内部类中的静态最终字段
【发布时间】:2015-02-24 16:59:37
【问题描述】:

当我尝试编译以下代码时,出现编译错误:

unexpected type System.out.println( new Test().C.i );
                                    ^
required: class,package 
found: value

class Test {

    class C {
        static final int i = 0;    
    }

    public static void main(String... z) {
        System.out.println( new Test().C.i  );
    }

}

但是,如果我将new Test().C.i 更改为new Test().new C().i,它编译就好了。

为什么?如果 i 在 C 中是静态的,那么我不应该实例化 C。我应该能够通过类 C 调用它,而不是 C 对象。

我错过了什么?

【问题讨论】:

  • 您不能通过变量访问内部类型(静态或非静态)。如果您创建Test t = new Test();,那么t.C 是不恰当的调用类型C。您只能使用Test.C.i 之类的外部类型或使用new 来实际创建C 的实例,这将让您访问i(我确信有一些Java 语言规范可以解释它,希望有人能找到并发布)。
  • 可以通过变量访问静态嵌套类。如果 C 和 i 都是静态的,Test.C.i 就可以正常工作。
  • Test.C Test 不是变量,而是类型。在我的示例中,t 是可变的。
  • 今天的问题中,投票最多的获胜者是......
  • 啊,你是对的。对不起。

标签: java class variables


【解决方案1】:

我认为new Test().new C().i 起作用的原因是因为类Test 是顶级类并且被视为static。如果您要将内部类 C 更改为静态,那么 new C().i 将起作用。

但是,您应该以非静态方式访问静态成员。

要访问您的静态字段,请执行以下操作:

System.out.println(C.i);

编辑:

对于那些说类 Test 不是静态的,请参考这个 stackoverflow answer

根据定义,所有顶级类都是静态的。

静态归结为类的实例可以 独立存在。或者,反过来:非静态内部类 (=实例内部类)没有外部实例就不能存在 班级。由于顶级类没有外部类,它不能 绝不是静态的。

因为所有顶级类都是静态的,所以在 顶级类定义毫无意义。


只是为了向您展示以这种方式访问​​静态字段是多么愚蠢的想法,我创建了以下项目:

class Test {

    class C {
        static final int i = 0;
    }

    public static void main(String[] args) {
        // BAD:
        System.out.println(new Test().new C().i);
        // Correct:
        System.out.println(C.i);
    }

}

如果你编译类并在jd-gui查看它,你可以看到它是如何编译的:

class Test {

  public static void main(String[] args) {
    void tmp13_10 = new Test(); tmp13_10.getClass(); new C(); System.out.println(0);
    System.out.println(0);
  }

  class C {
    static final int i = 0;

    C() {
    }
  }
}

【讨论】:

  • 无需将C 更改为static 即可通过Test.C.i 访问i。这也不是 OP 所要求的。
  • 这不是我要问的。我已经知道了。我问的是学术原因。
  • @Pshemo 我的错,你是对的。正在用我的手机接听。
  • @frankie 解释了为什么它适用于new Test().new C().i
  • "new Test().new C().i 有效是因为Test 是静态类" 看起来不太好,因为Test 不是静态的。 "* 如果您要将内部类 C 更改为静态,那么 new C().i 将起作用*" 是的,但仅在 Test 类的静态方法中,因为编译器会将其更改为 new Test.C().i 但问题是为什么 new Test().C.i不起作用,所以看起来你的答案仍然没有回答问题。
【解决方案2】:

问题在于 Java 语法中的 "." Identifier 期望标识符引用变量而不是类型。

这是在 JLS 的6.5.6.2. Qualified Expression Names 中指定的(以及其他地方):

如果 Q 是命名类类型的类型名称(第 8 节(类)),则:

如果类中没有一个可访问的(第 6.6 节)成员 类型为名为 Id 的字段,则发生编译时错误。

否则,如果单个可访问成员字段不是类 变量(也就是说,它没有声明为静态的),然后是编译时 发生错误。

否则,如果类变量声明为final,则Q.Id表示 类变量的值。

表达式 Q.Id 的类型是类的声明类型 捕获转换后的变量 (§5.1.10)。

如果 Q.Id 出现在需要变量而不是值的上下文中, 然后发生编译时错误。

否则,Q.Id 表示类变量。

表达式 Q.Id 的类型是类的声明类型 捕获转换后的变量 (§5.1.10)。

请注意,此子句涵盖了枚举常量的使用(第 8.9 节),因为 这些总是有一个对应的最终类变量。

虽然我绝对可以理解为什么你会认为它会这样工作的逻辑 - 我实际上希望它也能工作 - 这没什么大不了的:因为总是只有一个 i 你可以通过Test.C.i参考。如果i 是非静态new Test().new C().i 将是访问它的正确方法。

查看它的另一种方法是阅读15.8. Primary Expressions,它具有主要表达式的实际语法(这是我们在这里处理的内容):它允许ClassInstanceCreationExpression(这就是new Test().new C().i工作的原因)以及FieldAccess(适用于Test.C.i,因为“类”是递归解析的——只有最后一个标识符必须引用一个字段)。

【讨论】:

  • 我认为您输入了错误的 JLS 条目。您想要Q 是表达式名称,而不是类型名称的那个。
  • @Sotirios 我合理地需要这两个地方,可能还有很多其他地方来涵盖所有组合。我认为 15.8 总体上更加清晰 - 关于语言语法的问题最终总是通过查看语法得到最好的回答。我可能会进一步扩展后一点并删除第一部分。
  • 我宁愿停止关注句法问题(大多数现代 IDE 都会自己解决)并解释为什么它真的不起作用 - 请参阅下面的答案
  • @rgasiore OP 知道静态/非静态类的作用,并多次表达了她对 为什么 sn-p 不起作用的学术兴趣(这将是合理的所有人都期望只能通过外部类的实例来引用非静态内部类)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多