Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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");
        }
    }
}
like image 503
Ram Patra Avatar asked Apr 14 '15 06:04

Ram Patra


People also ask

What is the difference between inner class and local class?

inner class: Can only exist withing the instance of its enclosing class. Has access to all members. local class: class declared in a block. It is like an inner class (has access to all members) but it also has access to local scope.

What is true about method local inner class?

Option B is correct because a method-local inner class can be abstract, although it means a subclass of the inner class must be created if the abstract class is to be used (so an abstract method-local inner class is probably not useful).

Can method local inner class be static?

Therefore, the declaration of method local inner class cannot use any access modifiers such as public, protected, private, and non-access modifiers such as static.

Is a nested class A local class?

Local Class and Nested class are different beasts. A Nested class is a class declared within the scope of another class. A Local class is declared within a function definition.


3 Answers

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.

like image 111
Rohit Jain Avatar answered Oct 21 '22 15:10

Rohit Jain


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.

like image 23
Tim Biegeleisen Avatar answered Oct 21 '22 15:10

Tim Biegeleisen


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.

like image 7
Radiodef Avatar answered Oct 21 '22 15:10

Radiodef