Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 534
Tiny Avatar asked Oct 13 '13 19:10

Tiny


People also ask

Can we access all the members of outer class inside a member inner class?

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.

How do you access the outer class variable in an inner 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.

Can an inner class extend its outer class?

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.

Can inner class access outer class private variables Python?

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.


2 Answers

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

like image 63
Rohit Jain Avatar answered Nov 15 '22 19:11

Rohit Jain


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.

like image 34
Sotirios Delimanolis Avatar answered Nov 15 '22 17:11

Sotirios Delimanolis