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

Yurii Koval :

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)

Sotirios Delimanolis :

The Java Language Specification (JLS), in the context of the compiler resolving a method invocation expression, states

If the form is super . [TypeArguments] Identifier, then the class to search is the superclass of the class whose declaration contains the method invocation.

The class whose declaration contains the method invocation, in this case, is the anonymous Solution subclass and its superclass is Solution. The JLS, in the context of determining which instance will be used to invoke the method, then goes on to say

If the form is super . [TypeArguments] Identifier, then the target reference is the value of this.

this, in this case, refers to the instance of the anonymous Solution subclass. That instance's name field was initialized with the value "sout" as so that is what getName() returns.


In the original sample,

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

the getName() method invocation is unqualified and therefore a different rule applies. That is

If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.

T, here, is the Solution class, since it is the innermost enclosing type of the anonymous Solution subclass and getName() is its member.

Then, the JLS states

Otherwise, let T be the enclosing type declaration of which the method is a member, and let n be an integer such that T is the n'th lexically enclosing type declaration of the class whose declaration immediately contains the method invocation. The target reference is the n'th lexically enclosing instance of this.

Again, T is Solution, the 1st lexically enclosing type since the class whose declaration immediately contains the method invocation is the anonymous Solution subclass. this is the anonymous Solution subclass instance. The target reference is therefore the 1st lexically enclosing instance of this, ie. the Solution instance, whose name field was initialized with the value "main". That's why the original code prints "main".

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=35869&siteId=1