Accessing outer class members from within an inner class extending the outer class itself

12,845

Solution 1

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.

Solution 2

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

Solution 3

In the above case there are 2 different relations between TestInnerClass and InnerClass.

But there is a little twist in story.. there are two objects! and the problem is that we are putting "Another Value" into different object! and printing the value from the old one..

First Object:

public static void main(String[] args) {
    new TestInnerClass("Initial value").callShowValue();
}

Above method in Test Class creates an instance of TestInnerClass with "Initial value"

second Object:

public void callShowValue() {
    new InnerClass("Another value").showValue();
}

Since the InnerClass is extending the TestInnerClass another new Instance of TestInnerClass is created with "Another Value". But we are printing the value from old object not the second one.

Solution 4

The other answers have explained well why you are getting the results you see (you have two instances of TestInnerClass and are accessing the first), however there is actually a way to access the private members of TestInnerClass in its subclass - the keyword super.

If you replace your showValue method with this:

public void showValue() {
    System.out.println(super.getValue());
    System.out.println(super.value);
}

You will get the output that you expected.

I would also suggest that if you decide to do this you make InnerClass a static inner class because it no longer needs a reference to an instance of its outer class.

Share:
12,845
Tiny
Author by

Tiny

Just an orphan kid and have no more to say. Three things in general, cannot be avoided (at least I can never) Mother Mother-tongue Mother-land. They are always unique. I'm a family-less boy. My family was hunted leaving me all alone when my house targeted and deliberately set on a fire by a mob during a nonsense communal riot but I was survived by a rescue team with the help of firemen. As a survival, I didn't know whether it was my fortune or misfortune but when I recovered, the rescue team came to my home, one day. One of the members gave me a piece of paper in my hand in which the following text was written. lifeisnowhere. He asked me to read it carefully and I could hardly interpret the text as Life is now here, instead of Life is nowhere. All of them gave me a cute smile and went away and I decided to live peacefully and hopefully on their saying from then onwards and very soon. Because of this tragedy, I'm alone couldn't join a school but a curiosity to learn something made me a self-learner. I'm indeed a self-learner, so I'm likely not able to answer any questions on this site right now. In the field of computer science, my self-study mainly includes, QBASIC, C, C++, C#, VB, Java, JavaScript, PHP and a little about ASP.NET. Oracle, MySQL and MSSQL-Server with DBMS. and other theoretical subjects. I'm currently dealing with - Android and Java EE including Servlet, JSP-JSTL/EL (with Spring and Struts with ORM models JPA/Hibernate) and JSF.

Updated on July 24, 2022

Comments

  • Tiny
    Tiny almost 2 years

    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?

  • Sotirios Delimanolis
    Sotirios Delimanolis over 10 years
    Damn, dude. On a related note, I'm going to start using my name in my answers as well. :)