6

I couldn't understand the behavior of the below code.

My expectation was that it should print 100 in both println() methods. Why the result is different?

If I uncomment getX() method in inner class, then the output of println() methods are the same which is 100.

I couldn't figure out the reasoning of different behavior. Please help me to understand.

public class Foo {

    public static void main(String[] args) {
        MyOuter outerObj = new MyOuter();
        MyOuter.MyInner innerObj = outerObj.new MyInner();

        innerObj.setX();

        System.out.println("x: " + innerObj.getX()); //x: 3
        System.out.println("x: " + outerObj.getX()); //x: 100
    }
}
class MyOuter {
    private int x = 3;

    public int getX() { return x; }

    class MyInner extends MyOuter {
        public void setX(){ x = 100; }
//      public int getX() { return x; }
    }
}
Mikheil Zhghenti
  • 734
  • 8
  • 28
Alok
  • 61
  • 1
  • Your `MyInner` has access to **two** different instances of the `MyOuter` class, one being the outer class, accessible using `MyOuter.this`, and one being the base class, accessible using `super`. `innerObj.getX()` calls the `getX()` method inherited from the base class. Seems the unqualified `x` accesses the `MyOuter.this` instance. For your code to make *any* sense, remove `extends Outer`. – Andreas Sep 18 '20 at 22:23
  • 1
    *FYI:* I'm an experienced Java programmer, but if you had asked me whether an unqualified access would access the `super` instance or the `MyOuter.this` instance, I would have needed to consult the manual to answer that. Having the same class as both outer class and base class is just wrong, and should never happen in real life. If it did, the code should darn well always qualify field access, to ensure everybody understands what the code is doing. I would have preferred if the compiler would have failed with an "ambiguois access" error message. – Andreas Sep 18 '20 at 22:24
  • @Andreas the behaviour is actually in-line with the [behaviour of `protected` in the context of a static method situated in a child class](https://stackoverflow.com/questions/30568031/why-protected-method-is-not-accessible-from-subclass). – Turing85 Sep 18 '20 at 22:30
  • @Turing85 LOL, we commented to each other at the same time. I missed the `private` part. --- *Curious:* Before today, would you have been able to answer whether unqualified access to `x` means `super.x` or `MyOuter.this.x` if the field hadn't been `private`? – Andreas Sep 18 '20 at 22:33

1 Answers1

4

From MyInner's perspective, there are two private int x fields in play here:

  • One field super.x that is inherited from MyOuter, but inaccessible because it is declared as private, and MyInner as an inheriting object cannot access this field.
  • One field MyOuter.this.x from the sorrounding MyOuter-instance (since MyInner is a (non-static) inner class, it is always bound to an instance of the surrounding MyOuter), that is accessible.

The method setX() in MyInner cannot access the inherited field super.x, thus it accesses the field MyOuter.this.x and sets its values to 100. A consecutive call on the surrounding MyOuter's getX() will return 100.

The call innerObj.getX() (which is inherited from MyOuter and can access super.x) returns the value of the inherited field super.x (still having its initial value of 3).

If we remove the extends MyOuter from and include the getX()-method in MyInner, the code behaves as expected.


The fact that MyInner cannot access the inherited field private x is confusing at first, but the behaviour is actually in line with the behaviour of the keyword protected in the context of static mehthods in inheriting classes as discussed in this post by Hariharan

Turing85
  • 18,217
  • 7
  • 33
  • 58
  • Ahhh, `private`, I missed that detail, and that's why `x = 100` means `MyOuter.this.x = 100`, not `super.x = 100`. If you remove `private`, `x = 100` suddenly means `super.x = 100`. You might want to cover that in your answer too, for completeness. With ambiguity, `super` wins over outer class access, which does make sense, but still... – Andreas Sep 18 '20 at 22:30
  • @Andreas ah I forgot the syntax of `MyOuter.this...`. Updated my answer. Thanks for the comment! – Turing85 Sep 18 '20 at 22:36
  • *Nitpick:* "nonstatic, inner class" --- Inner classes are [by definition](https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.3) non-static: *"An inner class is a nested class that is not explicitly or implicitly declared `static`"*. --- But, reminding people is ok, so leave it... My OCD has been satisfied by this comment. ‍♂️ – Andreas Sep 18 '20 at 22:39
  • @Andreas I know ;) but especially in answers, I like to clarify the situation. Set it in parentheses :) – Turing85 Sep 18 '20 at 22:41