简体   繁体   English

方法本地内部类与内部类

[英]Method local inner class vs inner class

The below code produces the output middle . 下面的代码生成输出middle Can anyone explain in detail how this is happening? 谁能详细解释这是怎么回事?

Is it because the declaration of "inner" version of class A comes after the instance of class A is created in the go() method? 是因为class A的“内部”版本的声明是在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");
        }
    }
}

I guess you expected the local class method to be invoked. 我猜你期望调用本地类方法。 That didn't happen, because you're using new A() outside the scope of local class. 这没有发生,因为你在本地类的范围之外使用new A() So, it accesses the next closer candidate in scope, that would be the inner class. 因此,它访问范围中的下一个更接近的候选者,即内部类。 From JLS §6.3 : 来自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. 由块(第14.2节)直接包含的本地类声明的范围是直接封闭块的其余部分,包括它自己的类声明。

Thus, new A() in the first line of method, will not access the local class appearing after it. 因此,方法第一行中的new A()将不会访问其后出现的本地类。 If you move the class declaration before that, you'll get the expected output. 如果在此之前移动类声明,您将获得预期的输出。

Also see JLS §14.3 , which contains similar example. 另请参阅JLS§14.3 ,其中包含类似示例。

You are getting the output "middle" because of the order in which you have your code. 由于您拥有代码的顺序,您将获得输出“中间”。 Since the method-scoped class A occurs after your call to new A() , you are getting the output "middle". 由于方法范围的class A 您调用new A() 之后发生,因此您将获得输出“middle”。 If you switch around the order as follows, you will get the output "inner": 如果您按如下方式切换订单,您将获得输出“内部”:

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

Output: 输出:

inner

The order of precedence for instantiating class A , from high to low, is: 实例化class A的优先顺序,从高到低,是:

  1. block
  2. method 方法
  3. class
  4. package

Please have a look at the official Java Language Specification discussing inner classes for more information. 有关更多信息,请查看讨论内部类的官方Java语言规范

The reason inner doesn't get printed is ( 6.3 ): 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. 由块直接包含的本地类声明的范围是直接封闭块的其余部分,包括它自己的类声明。

(A class declared inside a method is called a local class.) (在方法内声明的类称为本地类。)

So A can't refer to the local class, because the expression new A() happens before its declaration. 所以A不能引用本地类,因为表达式new A()在声明之前发生。 In other words, local classes have similar scope to local variables. 换句话说,本地类与局部变量具有相似的范围。

The reason middle gets printed instead of outer is that the inner class A shadows the top-level class A ( 6.4.1 ): middle打印而不是outer的原因是内部类A 影响顶级类A6.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 . 的声明d命名类型的n阴影任何其他类型的命名的声明n是在范围[...]的d

This means that anywhere in the body of MethodLocalVSInner , the unqualified A must refer to the inner class. 这意味着MethodLocalVSInner正文中的任何位置,非限定A必须引用内部类。

If you are familiar with shadowing of member variables eg: 如果您熟悉成员变量的阴影,例如:

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

Essentially the same thing is going on with class declarations. 基本上同样的事情是继续进行类声明。

Case 1: 情况1:

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

In this case if you running your method outside the scope of local class. 在这种情况下,如果您在本地类的范围之外运行您的方法。 That why it will print middle 这就是它打印middle

Case 2: 案例2:

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

In this case it will print inner becase class is now in the scope. 在这种情况下,它将打印inner becase类现在在范围内。

in method : 在方法中:

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

when method is started executing, first line will be executed new A().m(); 当方法开始执行时,第一行将执行new A().m();

and because inner class is already in scope so object for that class will be created and m method will be called for inner class not for local method class because its still not in scope. 并且因为内部类已经在范围内,所以将创建该类的对象,并且将为inner class调用m方法而不是local method class因为它仍然不在范围内。 that is why you are getting middle as output. 这就是为什么你得到middle作为输出。

but if you change your method as : 但如果您将方法更改为:

 void go() {

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

Your local method class will now be in scope and will have higher preference so you will get output now inner . 您的本地方法类现在将在范围内并具有更高的首选项,因此您现在将获得输出inner

You are calling go method using an instance of MethodLocalVSInner 您正在使用MethodLocalVSInner的实例调用go方法

Inside the go method you are creating an instance of A() here since you are not explicitly import the outer A class and immediate inner class is after the method calling statement, JVM is picking the inner class A which is in the class level of MethodLocalVSInner and execute the go method inside that 在go方法中你要创建一个A()实例,因为你没有显式地导入外部A class而直接内部类是在方法调用语句之后,JVM正在选择MethodLocalVSInner类级别的inner class A并在其中执行go方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM