问题 枚举定义中的静态上下文


Java提供的语法糖 enum 设施有时会有点混乱。考虑这个不编译的例子:

public enum TestEnum {

    FOO("foo") {
        public void foo() {
            helper();  // <- compiler error
        }
    };

    String name;
    TestEnum(String name) {
        this.name = name;
    }

    public abstract void foo();

    private void helper(){
        // do stuff (using this.name, so must not be static)
    }
}

任何人都可以解释为什么编译器说

无法从静态上下文引用非静态方法'helper()'

这个上下文究竟是如何静态的?

您可以通过将调用更改为来进行编译 this.helper() (这里有一个令人困惑的观点:如果我们真的像编译器建议的那样处于“静态上下文”中,怎么能“this“工作?”或通过增加可见性 helper() 到默认级别。你更喜欢哪个?另外,随意提出更好的问题标题:-)

编辑: 我发现 关于此的一些讨论  - 但没有真正的答案。我的同事认为这个事实 this.helper() works实际上是一个编译器bug。事实上,似乎有更新的Java版本  工作(虽然 super.helper() 确实):“找不到符号助手()”。 (虽然有一些奇怪的事情发生了:尝试使用不同的Java版本后我无法获得 this.helper() 用它们中的任何一个再次编译...)


2459
2018-02-24 11:37


起源



答案:


错误消息具有误导性,只是make helper 受保护,它会工作。

protected void helper(){
    // can be called from subclasses (such as FOO) since it is not private
}

8
2018-02-28 15:52





Java Puzzlers一书中介绍了类似的内容。 IIRC,在超类之前总是会考虑外部类的上下文。在这种情况下,找到帮助者。但我们正在静态环境中构建价值(实际上是一个 private static final 之前 FOO)。因此错误。

尝试 super.helper();


3
2018-02-24 12:13



我有这本书;你的意思是哪个谜题? (我找不到很快。) - Jonik
我的这本书的副本都装在盒子里的某个地方,所以我不知道。 - Tom Hawtin - tackline


答案:


错误消息具有误导性,只是make helper 受保护,它会工作。

protected void helper(){
    // can be called from subclasses (such as FOO) since it is not private
}

8
2018-02-28 15:52





Java Puzzlers一书中介绍了类似的内容。 IIRC,在超类之前总是会考虑外部类的上下文。在这种情况下,找到帮助者。但我们正在静态环境中构建价值(实际上是一个 private static final 之前 FOO)。因此错误。

尝试 super.helper();


3
2018-02-24 12:13



我有这本书;你的意思是哪个谜题? (我找不到很快。) - Jonik
我的这本书的副本都装在盒子里的某个地方,所以我不知道。 - Tom Hawtin - tackline


如果我将你的枚举翻译成它的类结构,它看起来大致如下:

public abstract class TestEnum {

  public static final TestEnum FOO = new FOO("foo") {
    public void foo() {
        helper();  // <- compiler error
    }
 };

  String name;
  TestEnum(String name) {
      this.name = name;
  }

  public abstract void foo();

  private void helper(){
    // do stuff (using this.name, so must not be static)
  }

}

实例FOO是扩展TestEnum的任何类。这就是我相信你无法访问helper()的原因,因为它是私有的。所以this.helper()可能不应该工作。我不确定为什么即使super.helper()也可以工作,但是enum可以让你私有访问父级。

至于静态上下文错误,我同意错误消息似乎没有意义。


2
2018-02-24 14:24



Nitpick:用“新FOO”你的意思是“新的TestEnum”吗? - Jonik
是的,也抓住了..谢谢 - richs


您可以将每个枚举常量视为枚举类的子类的唯一实例。就像“常规”类一样,枚举“子类”不能访问枚举“超类”的私有成员。 (我不能重新创建你提到的“这个”。)

一个合理的解决方案是 更改方法访问权限 私人的 至 保护 允许访问枚举常量“子类”。

更好的问题标题建议:“Java私有枚举方法?”或者只是“私有枚举方法”(让Java标记处理Java-ness)


1
2018-05-21 03:56





有一个 文章 提到这个问题,不幸的是它是德语。也许你仍然可以提取一些有用的信息。

编辑:似乎与你的问题有些不同。我仍然不明白你的例子的行为,我也在这里看到。


0
2018-02-24 12:04