方法局部内部 class 与内部 class

Method local inner class vs inner class

下面的代码产生输出 middle。谁能详细解释这是怎么回事?

是不是因为class A的"inner"版本的声明是在go()方法中创建了class A的实例之后?

class A {
    void m() {
        System.out.println("outer");
    }
}

public class MethodLocalVSInner {
    public static void main(String[] args) {
        new MethodLocalVSInner().go();
    }

    void go() {
        new A().m();
        class A {
            void m() {
                System.out.println("inner");
            }
        }
    }

    class A {
        void m() {
            System.out.println("middle");
        }
    }
}

我猜您希望调用本地 class 方法。这并没有发生,因为您在本地 class 范围之外使用 new A()。因此,它访问范围内的下一个更接近的候选者,即内部 class。来自 JLS §6.3:

The scope of a local class declaration immediately enclosed by a block (§14.2) is the rest of the immediately enclosing block, including its own class declaration.

因此,第一行方法中的new A(),将不会访问其后出现的本地class。如果您在此之前移动 class 声明,您将获得预期的输出。

另请参阅 JLS §14.3,其中包含类似示例。

您得到的输出是 "middle",因为您拥有代码的顺序。由于方法作用域 class A 在您调用 new A() 之后 发生 ,您将获得输出 "middle"。如果按如下方式切换顺序,您将得到输出 "inner":

void go() {
    class A {
        void m() {
            System.out.println("inner");
        }
    }
    new A().m();
}

输出:

inner

实例化class A的优先顺序从高到低是:

  1. 阻止
  2. 方法
  3. class
  4. 套餐

请查看官方Java Language Specification discussing inner classes了解更多信息。

您正在使用 MethodLocalVSInner

的实例调用 go 方法

go 方法内部 您正在创建 A() 的实例 在这里,由于您没有显式导入外部 A class 并且直接内部 class 在方法调用语句之后,JVM 选择 inner class A ,它位于 class 级别 MethodLocalVSInner 并在里面执行 go 方法

案例 1:

void go() {
        new A().m();
        class A {
            void m() {
                System.out.println("inner");
            }
        }
    }

在这种情况下,如果您 运行 您的方法超出了本地 class 的范围。这就是为什么它会打印 middle

案例 2:

void go() {
        class A {
            void m() {
                System.out.println("inner");
            }
        }
        new A().m();
    }

在这种情况下,它将打印 inner 因为 class 现在在范围内。

在方法中:

 void go() {
    new A().m();
    class A {
        void m() {
            System.out.println("inner");
        }
    }
}

当方法开始执行时,第一行将被执行 new A().m();

并且因为内部 class 已经在范围内,所以将创建 class 的对象,并且将为 inner class 而不是 [=15] 调用 m 方法=] 因为它仍然不在范围内。这就是为什么你得到 middle 作为输出。

但如果您将方法更改为:

 void go() {

    class A {
        void m() {
            System.out.println("inner");
        }
    }
   new A().m();
}

您的本地方法 class 现在将在范围内并且具有更高的优先级,因此您现在将获得输出 inner

未打印 inner 的原因是 (6.3):

The scope of a local class declaration immediately enclosed by a block is the rest of the immediately enclosing block, including its own class declaration.

(在方法内部声明的 class 称为局部 class。)

所以A不能引用局部class,因为表达式new A()发生在它的声明之前。换句话说,局部 classes 具有与局部变量类似的作用域。

打印 middle 而不是 outer 的原因是内部 class A shadows 是顶层class A (6.4.1):

A declaration d of a type named n shadows the declarations of any other types named n that are in scope […] of d.

这意味着在MethodLocalVSInner的主体中的任何地方,不合格的A必须引用内部class。

如果您熟悉成员变量的隐藏,例如:

class Example {
    int x;
    void setX(int x) {
        //       ┌ 'x' refers to the local method parameter
        this.x = x;
    }
}

class 声明本质上是一样的。