【问题标题】:Java Language Specification - Cannot understand 'BlockStatement'Java 语言规范 - 无法理解“BlockStatement”
【发布时间】:2011-07-09 20:15:20
【问题描述】:

我一直在研究 Java 语言规范 here(我应该出去喝杯啤酒),我很好奇方法可以包含什么。规范声明方法体可以包含一个块

MethodBody:
    Block

“块”包含“块语句”。 'BlockStatement' 规则如下所示:

BlockStatement : 
    LocalVariableDeclarationStatement
    ClassOrInterfaceDeclaration
    [Identifier :] Statement

我可以理解“LocalVariableDeclarationStatement”,它可能是

[final] int x, y, z;

但是,我不明白为什么存在“ClassOrInterfaceDeclaration”规则。该规则如下所示:

ClassOrInterfaceDeclaration: 
    ModifiersOpt (ClassDeclaration | InterfaceDeclaration)

ClassDeclaration: 
    class Identifier [extends Type] [implements TypeList] ClassBody

InterfaceDeclaration: 
    interface Identifier [extends TypeList] InterfaceBody

这里发生了什么 - 你不能肯定地在一个块中声明一个类或接口? 有人可以帮助澄清这种困惑吗?

更新:我可以在一个方法中定义一个类,但是下面的不行:

public class Foo {
    public void doFoo() {
        interface dooJa {
            int bar();
        }
    }
}

编译器抱怨说“成员接口 dooJa 只能在*类或接口中定义”...有什么解释吗?

【问题讨论】:

    标签: java language-features


    【解决方案1】:

    哦,是的,您可以在方法体内声明一个类。 :-)

    class A {
    
        public void doIt() {
            class B {}
            B b = new B();
            System.out.println(b.getClass());
        }
    
    }
    

    【讨论】:

    • 好吧,我会......我在想某种匿名内部类或其他东西。谢谢你。
    • 顺便说一句 - 我认为我在 Eclipse 中尝试这个是主动的。但是它给了我一个错误,因为我使用了“公共”标识符,这是不允许的(仅抽象或最终),但我得到了错误,只是假设(不检查)你不能在方法。
    • 在方法中定义接口对我不起作用。在 Eclipse 中,我收到一条错误消息:“成员接口 dooJa 只能在*类或接口中定义”。然而,定义一个类确实有效。
    • 嗯,你总是可以使用匿名内部类在封闭方法中创建接口的实例,不是吗?
    • 但是你不能返回它(除非你打算返回一个对象),因为外界不知道这个接口。另外,执行方法范围的合同有什么意义? :)
    【解决方案2】:

    您已经很好地观察到接口不再工作了。原因是您正在查看非常旧的语法版本。它看起来已经超过10岁了。看看 Java 6 的语法(您可能正在测试):

    http://www.it.bton.ac.uk/staff/rnb/bosware/javaSyntax/rulesLinked.html#BlockStatement

    你会看到块语句:

    块语句: LocalVariableDeclarationStatement 类声明 声明

    【讨论】:

    【解决方案3】:

    具有内部类声明的块的示例:

    public class Test {
    
        static 
        {
            class C {}
            C c = new C();
        }
    }
    

    虽然我怀疑你会找到一个用例......

    【讨论】:

      【解决方案4】:

      正如其他人所说,您可以在方法中声明一个类。一个用例是使用它作为匿名内部类的替代方案。匿名内部类有一些缺点;例如,您不能在匿名内部类中声明构造函数。使用在方法中本地声明的类,您可以。

      这是一个愚蠢的例子,它并没有真正说明你为什么要这样做,但至少你可以如何做到。

      public Runnable createTask(int a, int b) {
          // Method-local class with a constructor
          class Task implements Runnable {
              private int x, y;
      
              Task(int x, int y) {
                  this.x = x;
                  this.y = y;
              }
      
              @Override
              public void run() {
                  System.out.println(x + y);
              }
          }
      
          return new Task(a, b);
      }
      

      【讨论】:

      • 确实如此,但即使语言规范说您也可以定义接口,这似乎也不适用于接口。
      【解决方案5】:

      这些被称为本地类。我偶尔使用它,但这并不是必需品。

      编辑:本地类可以是静态的,如果它出现在静态上下文中,例如,在静态方法中。

      根据规范的措辞,local/inner/anno classe 始终仅表示 class,而不是 interface

      【讨论】: