简体   繁体   中英

Method local inner class vs inner class

The below code produces the output 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 {
    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. So, it accesses the next closer candidate in scope, that would be the inner class. From 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.

Thus, new A() in the first line of method, will not access the local class appearing after it. If you move the class declaration before that, you'll get the expected output.

Also see JLS §14.3 , which contains similar example.

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". 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:

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

Please have a look at the official Java Language Specification discussing inner classes for more information.

The reason inner doesn't get printed is ( 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. 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 ):

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

This means that anywhere in the body of MethodLocalVSInner , the unqualified A must refer to the inner class.

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:

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

Case 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.

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();

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. that is why you are getting middle as output.

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 .

You are calling go method using an instance of MethodLocalVSInner

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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