In the code snippet shown below, an inner class inherits an outer class itself.
package test;
class TestInnerClass {
private String value;
public TestInnerClass(String value) {
this.value = value;
}
private String getValue() {
return value;
}
public void callShowValue() {
new InnerClass("Another value").showValue();
}
private final class InnerClass extends TestInnerClass {
public InnerClass(String value) {
super(value);
}
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
}
}
public final class Test {
public static void main(String[] args) {
new TestInnerClass("Initial value").callShowValue();
}
}
The only statement inside the main()
method (the last snippet) assigns the value Initial value
to the private field value
of the TestInnerClass
class and then invokes the callShowValue()
method.
The callShowValue()
method causes another string - Another value
to be set the to the private field value
of the TestInnerClass
class before invoking the showValue()
method of InnerClass
extending TestInnerClass
.
Accordingly, the following two statements inside the showValue()
method,
System.out.println(getValue());
System.out.println(value);
should display,
Another value
Another value
But they display,
Initial value
Initial value
Why does this happen?
You can access any field of outer class from inner class directly. Even Outer class can access any field of Inner class but through object of inner class. Show activity on this post. "A nested class is a class defined within another class.
If you want your inner class to access outer class instance variables then in the constructor for the inner class, include an argument that is a reference to the outer class instance. The outer class invokes the inner class constructor passing this as that argument.
Inner class can extend it's outer class. But, it does not serve any meaning. Because, even the private members of outer class are available inside the inner class. Even though, When an inner class extends its outer class, only fields and methods are inherited but not inner class itself.
Bookmark this question. Show activity on this post. private This is the default, and means that the method or variable is accessible only within the Apex class in which it is defined.
The key here is to understand how inner classes accesses the members of outer classes. And how is access to those members qualified in case of private
and non-private
members. (Note: I'll talk about non-static
inner classes here, as question is about that only).
Inner classes store reference to enclosing instance:
An inner class stores a reference to the enclosing instance as a field. The field is named as this$0
. The enclosing instance is always bound to the inner class object. When you create an object of inner class from inside the enclosing class, the reference value of this$0
remains same for all of them, but this
reference would differ.
You access this$0
field using Outer.this
syntax in the inner class. For example, consider this code:
class Outer {
public Outer() { }
public void createInnerInstance() {
Inner obj1 = new Inner();
Inner obj2 = new Inner();
}
private class Inner {
public Inner() {
System.out.println(Outer.this);
System.out.println(this);
}
}
}
public static void main(String[] args) {
new Outer().createInnerInstance();
}
When you execute this code, you will get output like this:
Outer@135fbaa4
Outer$Inner@45ee12a7
Outer@135fbaa4
Outer$Inner@330bedb4
Notice how 1st and 3rd references are same, while 2nd and 4th are different.
Outer class members can be accessed in inner class using this$0
reference:
When you access the fields or any other member of outer class from inner class, the access expression is qualified with automatically this$0
. You explicitly qualify the member access as this$0
using OuterClass.this
reference. So, consider field value
in your outer class was public
, then in the showValue()
method in your inner class:
public void showValue() {
System.out.println(TestInnerClass.this.value);
System.out.println(value);
}
the first 2 print statements are equivalent. They would be compiled to the same byte code:
public void showValue();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/
o/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LTestInnerClass;
7: getfield #4 // Field TestInnerClass.value:Ljava/lang/Stri
g;
10: invokevirtual #5 // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
13: getstatic #3 // Field java/lang/System.out:Ljava/
o/PrintStream;
16: aload_0
17: getfield #1 // Field this$0:LTestInnerClass;
20: getfield #4 // Field TestInnerClass.value:Ljava/lang/Stri
g;
23: invokevirtual #5 // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
26: return
You cannot access the outer class members explicitly using this
:
If you explicitly try to qualify the field or method access expression with this
in the inner class, you will get a compiler error:
public void showValue() {
System.out.println(this.value); // this won't compile
}
The above print statement won't compile, because value
is not a field of the inner class itself. It's an outer class field. this
refers to the inner class instance, not the outer one.
Story changes when inner class extends outer class:
When your inner class extends the outer class, then is when things start to go weird. Because in that case, qualifying the field or method access with this
will be valid for non-private members. For private
members, that would still not be valid, as private
members are not inherited.
In case of inherited inner class, directly accessing the outer class members are qualified with this
. That means, they will be accessed as inner class members. While explicitly qualifying the access with Outer.this
will refer to the field of enclosing instance - this$0
.
Considering value
field is declared as public
:
public void showValue() {
System.out.println(value); // inner class instance field
System.out.println(this.value); // inner class instance field
System.out.println(Outer.this.value); // enclosing instance field
}
the first two print statement will print the value
field of the inner class instance, while the 3rd print statement will print the value
field of enclosing instance. Confused?
Remember I said, when you create multiple instance of inner class from inside the outer class, they will have same this$0
reference.
Consider you create an outer class instance:
new Outer("rohit").callShowValue();
and then in callShowValue()
method, you create an instance of inner class:
new Inner("rj").showValue();
Now, the output of the showValue()
method would be:
rj
rj
rohit
You would notice that, this.value
is different from Outer.this.value
.
What if you make value
field private
:
Now, when you make the outer class field private
, then of course you can't access it using this.value;
. So, the 2nd print statement won't compile.
And direct access of field in that case would be qualified with this$0
this time. now change the field value
private, and modify the showValue()
method as:
public void showValue() {
System.out.println(value); // enclosing instance field
System.out.println(this.value); // compiler error
System.out.println(Outer.this.value); // enclosing instance field
}
This is where the problem lies. The first print statement qualifies value
with this
or this$0
based on whether the field is public
or private
.
Coming to your concrete problem:
Now in your code, since both value
field and getValue()
method is private
, the showValue()
method:
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
is same as:
public void showValue() {
System.out.println(TestInnerClass.this.getValue());
System.out.println(TestInnerClass.this.value);
}
which is accessing the field and method of enclosing instance. And the field is still Initial Value. And that is why the output is:
Initial Value
Initial Value
The method getValue()
and the field value
are both private
. As such, they are not accessible to any other classes, including sub-classes, ie. they are not inherited.
In InnerClass#showValue()
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
because of the fact that those are private, getValue()
and value
are referring to the outer class' members, which are accessible because you are in the same class, ie. inner classes can access outer class private members. The above calls are equivalent to
public void showValue() {
System.out.println(TestInnerClass.this.getValue());
System.out.println(TestInnerClass.this.value);
}
And since you've set value
as
new TestInnerClass("Initial value")
you see "Initial value"
being printed twice. There is no way to access those private
members in the sub-class.
The point is: don't make sub classes inner classes.
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