内部类对象

6

我无法理解以下代码的行为。

我原本期望两个 println() 方法都应该输出 100。为什么结果不同呢?

如果我取消注释内部类中的 getX() 方法,那么两个 println() 方法的输出将相同,都是 100。

我无法理解不同行为的原因,请帮助我理解。

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; }
    }
}

你的 MyInner 可以访问两个不同的 MyOuter 类实例,一个是外部类,可以使用 MyOuter.this 访问,另一个是基类,可以使用 super 访问。innerObj.getX() 调用了从基类继承来的 getX() 方法。似乎未经限定的 x 访问了 MyOuter.this 实例。要使代码有意义,请删除 extends Outer - Andreas
1
FYI:我是一名有经验的Java程序员,但如果你问我未经限定的访问是否会访问“super”实例还是“MyOuter.this”实例,我需要查阅手册才能回答。将同一个类既作为外部类又作为基类是错误的,在现实生活中永远不应该发生。如果真的发生了,代码必须明确限定字段访问,以确保每个人都理解代码正在做什么。我希望编译器会失败并显示“模棱两可的访问”错误消息。 - Andreas
@Andreas 实际上这种行为与子类中静态方法上下文中 protected 的行为是一致的。详情请参见:https://dev59.com/B4vda4cB1Zd3GeqPZ3pD。 - Turing85
@Turing85 哈哈,我们同时互相评论了。我错过了 private 部分。--- Curious: 在今天之前,如果这个字段不是 private,你能回答未经限定的访问 x 是指 super.x 还是 MyOuter.this.x 吗? - Andreas
1个回答

4

MyInner的角度来看,在这里有两个private int x字段:

  • 一个字段super.x是从MyOuter继承而来的,但由于它声明为private,因此不可访问,而且MyInner作为继承对象也无法访问该字段。
  • 一个字段MyOuter.this.x来自于周围的MyOuter-instance(因为MyInner是一个(非静态)内部类,它总是绑定到周围的MyOuter实例),因此是可以访问的。

MyInner中的方法setX()无法访问继承来的字段super.x,因此它访问了字段MyOuter.this.x并将其值设置为100。对周围的MyOutergetX()的后续调用将返回100

调用继承自MyOuter并可以访问super.xinnerObj.getX()返回继承来的字段super.x的值(仍然具有其初始值3)。

如果我们从中删除extends MyOuter并将getX()方法包含在MyInner中,则代码的行为符合预期。


MyInner无法访问继承的private x字段一开始会让人感到困惑,但实际上这个行为和在继承类中的静态方法上下文中使用关键字protected的行为是一致的,如Hariharan在这篇帖子中所讨论的那样


啊,private这个细节我错过了,这就是为什么x = 100意味着MyOuter.this.x = 100而不是super.x = 100。如果你去掉privatex = 100突然意味着super.x = 100。你可能也想在你的回答中涵盖这一点,以完整起见。在模糊情况下,super会优先于外部类访问,这是有道理的,但仍然…… - Andreas
@Andreas 啊,我忘记了 MyOuter.this... 的语法。我更新了我的答案。感谢您的评论! - Turing85
小问题:非静态内部类——按照定义,内部类已经是非静态的了:“内部类是一个嵌套的类,它没有明确或隐式地声明为static”。但是,提醒一下人们也是可以的,所以留着吧……我的强迫症被这个注释满足了。‍♂️ - Andreas
@Andreas 我知道 ;) 但特别是在答案中,我喜欢澄清情况。把它放在括号里 :) - Turing85

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接