Why do method() and super.method() refer to different things in an anonymous subclass?

  • A+
Category:Languages

I was solving some exercises to understand better how inner classes in java work. I found one quite interesting exercise. The condition of the exercise is to make printName() print "sout" instead of "main" with minimum changes. There is its code:

public class Solution {     private String name;      Solution(String name) {         this.name = name;     }      private String getName() {         return name;     }      private void sout() {         new Solution("sout") {             void printName() {                 System.out.println(getName());                 // the line above is an equivalent to:                 // System.out.println(Solution.this.getName);             }         }.printName();     }      public static void main(String[] args) {         new Solution("main").sout();     } } 

We've got an amusing situation - the two classes have is-A and has-A connections. It means that the anonymous inner class extends the outer class and also objects of the inner class have references to the objects of the outer class. If you run the code above, "main" will be printed. The child cannot invoke getName() of the parent through inheritance. But the child being inner class uses reference to parent(outer class) to access the method.

The simplest way to solve this task is to change the access modifier of getName() from private to anything else. So the child is able to use getName() through inheritance and thanks to late binding "sout" will be printed.

The another way to solve this task is to use super.getName().

private void sout() {     new Solution("sout") {         void printName() {             System.out.println(super.getName());         }     }.printName(); } 

And I cannot understand how it works. Can someone help me to understand this problem?

Thank you for trying)

 


The behavior may seem counter-intuitive but it becomes clear with a bit of refactoring.

So, the sout() method can actually be rewritten as

private void sout() {   new Solution("sout") {     void printName() {       String name = getName();       System.out.println(name);     }   }.printName(); }  public static void main(String[] args) {   Solution mainSolution = new Solution("main");   mainSolution.sout(); } 

Calling the sout() method of the mainSolution object, creates a child Solution object that has an additional printName() method, which calls

getName();

which only declared in the parent mainSolution object.

If getName() is declared as private, it is not overriden but it is still accessible from the inner class, so getName() refers to the name of the mainSolution, i.e. to main.

If getName() has no modifier, or is declared as protected or public, then it is inherited (overriden) and refers to the child Solution object's name, i.e. to sout, hence "sout" will be printed.

By replacing getName() in sout() with

Solution.this.getName() 

the string "main" will be printed in both scenarios.

By replacing it with either of

this.getName() super.getName() 

if the getName() method is declared as private a compilation error will occur, otherwise the string "sout" will be printed.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: