44

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");
        }
    }
}
10
  • 1
    Try to rename the inner class A to B and you'll get outer output. This should give you a hint. Commented Apr 14, 2015 at 6:45
  • 1
    @TimBiegeleisen I am going through a hard time choosing one particular answer as many of your answers are correct and to the point. Commented Apr 14, 2015 at 8:59
  • 1
    You will get a warning in eclipse IDE The type A is never used locally, That says local class A defined inside method go is never used. Commented Apr 14, 2015 at 9:13
  • I am curious to know for which requirement you are implementing this ? or its just a tricky question...!! Commented Apr 14, 2015 at 9:15
  • 1
    @KisHanSarsecHaGajjar Was solving some exercises from a book and I landed into this problem. Commented Apr 14, 2015 at 12:01

6 Answers 6

38

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.

Sign up to request clarification or add additional context in comments.

11 Comments

@Radiodef Well, what is missing?
@RohitJain Can you tell me why declaring the class before new A().m() brings it to the scope and it prints inner.
@KisHanSarsecHaGajjar Why do you write so many exclamation marks ?
@Ramswaroop The answer to your question may simply be that the Java architects chose local classes to behave this way.
@Ramswaroop I guess the JLS section which I quoted, about scope explains that pretty well.
|
16

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.

5 Comments

That's what the OP also guessed. But why is that? Please back up your answer.
Then find the JLS reference that proves it - currently it's just speculation.
@Tim Biegeleisen Can you tell me why declaring the class before new A().m() brings it to the scope and it prints inner?
As @Radiodef pointed out in his answer, as soon as you define the local class A in a block, the scope for that class is the remainder of the block after, but not before, that definition. As to why the Java architects decided to do it this way, that is another question.
@TimBiegeleisen Actually I wanted this to be included in the answer. Because this was what I wanted to know.
7

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.

Comments

4

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.

Comments

2

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.

2 Comments

This is not related to class loading. When the method is executed a second time, it will still generate the same output, even though the local class A is already loaded.
it because of scope preference also. which goes like method then class. @ErwinBolwidt i have change it now.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.