Ram Patra
Ram Patra

Reputation: 16674

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");
        }
    }
}

Upvotes: 44

Views: 3857

Answers (6)

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

Upvotes: 1

Radiodef
Radiodef

Reputation: 37875

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 e.g.:

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.

Upvotes: 7

Rohit Jain
Rohit Jain

Reputation: 213391

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.

Upvotes: 38

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522741

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.

Upvotes: 16

Prashant
Prashant

Reputation: 2614

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.

Upvotes: 2

Sumit Singh
Sumit Singh

Reputation: 15906

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.

Upvotes: 4

Related Questions