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");
}
}
}
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.
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).
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.
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.
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:
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 namedn
shadows the declarations of any other types namedn
that are in scope […] ofd
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With