Reputation: 16674
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
Reputation: 5712
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
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 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.
Upvotes: 7
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
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:
Please have a look at the official Java Language Specification discussing inner classes for more information.
Upvotes: 16
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
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