为什么method()和super.method()引用匿名子类中的不同内容?

Yur*_*val 15 java inheritance inner-classes

我正在解决一些练习,以更好地理解java中的内部类是如何工作的.我发现了一个非常有趣的运动.练习的条件printName()是以最小的变化打印"sout"而不是"main".有它的代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我们有一个有趣的情况 - 这两个类有-A和有-A连接.这意味着匿名内部类扩展了外部类,并且内部类的对象也引用了外部类的对象.如果您运行上面的代码,将打印"main".子进程无法getName()通过继承来调用父进程.但是作为内部类的子类使用对父(外部类)的引用来访问该方法.

解决这一任务的最简单方法是访问修饰符改变getName()private其他任何事情.因此,孩子能够getName()通过继承使用,并且由于后期绑定"sout"将被打印.

解决此任务的另一种方法是使用super.getName().

private void sout() {
    new Solution("sout") {
        void printName() {
            System.out.println(super.getName());
        }
    }.printName();
}
Run Code Online (Sandbox Code Playgroud)

我无法理解它是如何工作的.有人可以帮我理解这个问题吗?

谢谢你的尝试)

Sot*_*lis 10

Java语言规范(JLS)在编译器解析方法调用表达式的上下文中指出

如果表单是super . [TypeArguments] Identifier,则要搜索的类是其声明包含方法调用的类的超类.

声明中包含的方法调用,在这种情况下,是匿名的Solution子类,其超类是Solution.在确定将使用哪个实例来调用该方法的上下文中, JLS 继续说

如果表单是super . [TypeArguments] Identifier,则目标引用是值this.

this在这种情况下,指的是匿名Solution子类的实例.该实例的name字段初始化为值"sout",因此getName()返回.


在原始样本中,

new Solution("sout") {
    void printName() {
        System.out.println(getName());
    }
}.printName();
Run Code Online (Sandbox Code Playgroud)

所述getName()方法调用是不合格的,并且因此不同的规则适用.那是

如果有一个封闭类型声明,该方法是一个成员,T那么就是最里面的类型声明.要搜索的类或接口是T.

T这里是Solution类,因为它是匿名Solution子类的最内层封闭类型,并且getName()是它的成员.

然后,JLS声明

否则,让我们T成为该方法所属的封闭类型声明,并n设为一个整数,这T是一个类的第n个词法封闭类型声明,其声明立即包含方法调用.目标参考是第n个词汇封闭的实例this.

同样,TSolution第一个词法封闭类型,因为声明立即包含方法调用的Solution类是匿名子类.this是匿名Solution子类实例.因此,目标参考是第一个词汇封闭的实例this,即.的Solution情况下,其name场与值初始化"main".这就是原始代码打印的原因"main".


PNS*_*PNS 5

这种行为可能看似违反直觉,但通过一些重构就变得清晰了.

因此,该sout()方法实际上可以重写为

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();
}
Run Code Online (Sandbox Code Playgroud)

调用对象的sout()方法mainSolution,创建一个子Solution对象,该对象具有一个printName()调用的附加方法

getName();

它只在父mainSolution对象中声明.

如果getName()声明为私有,则它不会被覆盖,但它仍然可以从内部类访问,因此getName()引用的名称mainSolution,即to main.

如果getName()没有修饰符,或者声明为protected或public,那么它将被继承(覆盖)并引用子Solution对象的名称,即to sout,因此将打印"sout".

通过替换getName()sout()

Solution.this.getName()
Run Code Online (Sandbox Code Playgroud)

字符串"main"将在两个场景中打印.

用它中的任何一个替换它

this.getName()
super.getName()
Run Code Online (Sandbox Code Playgroud)

如果该getName()方法被声明为private编译错误将发生,否则将打印字符串"sout".