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

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

我正在解决一些练习以更好地理解 java 中的内部 class 是如何工作的。我发现了一个非常有趣的练习。练习的条件是使 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();
    }
}

我们有一个有趣的情况 - 两个 classes 有 is-A 和 has-A 连接。 这意味着匿名内部 class 扩展了外部 class 并且内部 class 的 objects 也引用了外部 class 的 objects ]. 如果您 运行 上面的代码,将打印 "main" 。 child 无法通过继承调用 parent 的 getName()。但是 child 是 inner class 使用对 parent(outer class) 的引用来访问该方法。

解决此任务的最简单方法是将 getName() 的访问修饰符从 private 更改为其他任何内容。所以 child 能够通过继承使用 getName() 并且由于后期绑定 "sout" 将被打印出来。

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

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

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

感谢您的尝试)

Java 语言规范 (JLS),in the context of the compiler resolving a method invocation expression,声明

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

声明包含方法调用的class,在本例中,是匿名Solution子class及其超class 是 Solution。 JLS,in the context of determining which instance will be used to invoke the method,接着说

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

this,在这种情况下,指的是匿名 Solution subclass 的实例。该实例的 name 字段已使用值 "sout" 初始化,因此这就是 getName() returns.


在原始样本中,

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

getName() 方法调用不合格,因此适用不同的规则。即

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,这里是 Solution class,因为它是匿名 Solution subclass 和 getName() 是它的成员。

然后,JLS 声明

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.

同样,TSolution,自 class 以来的第一个词法封闭类型,其声明立即包含方法调用是匿名 Solution subclass. this 是匿名 Solution subclass 实例。因此,目标引用是 this 的第一个词法封闭实例,即。 Solution 实例,其 name 字段已用值 "main" 初始化。这就是原始代码打印 "main".

的原因

该行为可能看起来违反直觉,但通过一些重构变得清晰。

所以,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();
}

调用 mainSolution 对象的 sout() 方法,创建一个子 Solution 对象,它有一个额外的 printName() 方法,调用

getName();

仅在父 mainSolution 对象中声明。

如果 getName() 被声明为私有的,它不会被覆盖但仍然可以从内部 class 访问它,所以 getName() 指的是 mainSolution,即 main.

如果 getName() 没有修饰符,或被声明为受保护或 public,则它被继承(覆盖)并引用子 Solution 对象的名称,即 sout,因此将打印 "sout"。

sout() 中的 getName() 替换为

Solution.this.getName()

字符串 "main" 将在两种情况下打印。

将其替换为

this.getName()
super.getName()

如果getName()方法声明为private会出现编译错误,否则会打印字符串"sout"。