Java中,在继承情况下,“this”实际上指的是什么?

8
为什么以下Java代码会产生:
10
superclass

问题中的代码是:

class SuperClass {
    int a;

    public SuperClass() {
        this.a = 10;
    }

    private void another_print() {
        System.out.println("superclass");
    }

    public void print() {
        System.out.println(this.a);
        this.another_print();
    }
}

class SubClass extends SuperClass {
    int a;

    public SubClass() {
        this.a = 20;
    }

    private void another_print() {
        System.out.println("subclass");
    }

    public void print() {
        super.print();
    }
}

public class Main {
    public static void main (String[] args) {
        SubClass c = new SubClass();
        c.print();
    }
}

没有创建SuperClass的实例,是吗? 不仅如此,Java甚至从SuperClass开始查找要调用的方法,它还以某种方式知道a = 10

让我们考虑类似的Python代码:

class SuperClass:
    def __init__(self):
        self.a = 10

    def another_prn(self):
        print('superclass')

    def prn(self):
        print(self.a)
        self.another_prn()

class SubClass(SuperClass):
    def __init__(self):
        self.a = 20

    def another_prn(self):
        print('subclass')

    def prn(self):
        super().prn()

c = SubClass()
c.prn()

它的工作方式与我期望的相同:

20
subclass

我的同事(不喜欢Java的Python用户)唯一给出的解释是:“Python不是真正的面向对象语言”。这个解释并不令人信服。

更新: private void another_print() 是我的失误,我应该使用protected


2
不同的是,Python 没有私有方法。 - njzk2
7
因为超类方法是私有的,所以你没有“覆盖(overriding)”它。同时,由于两个类都声明了变量a,因此它被隐藏了。 - George Mulligan
1
你可以自由地告诉你的同事,Java 也不是一个纯粹的面向对象编程语言(而 Python 可能是,尽管可能有其他人知道得更好)。 - E net4
3
重点不在于“this”指代什么,而在于编译器如何解析方法调用和字段名称。 - wero
1
我已经养成了在Java中包含@Override注释的习惯,即使它不是必需的。这样,当某些内容没有正确覆盖时,编译器会发出警告。 - OneCricketeer
显示剩余5条评论
4个回答

8
在子类的print方法中,你只需调用父类的print方法。所以它当然会打印出来来自父类的a。
在这里你有两个独立的a字段。字段不受覆盖影响,只有方法会被覆盖。父类有一个a字段,而子类中还有另一个a字段。
如果另一种语言产生另一个结果,那也不足为奇。此外,我不确定你的Python代码是否在逻辑上等同/类似于你的Java代码。

8
在Java中,构造函数的调用顺序很重要。 在SubClass中,当你实例化c时,构造函数隐式地调用了SuperClass的默认构造函数(public SuperClass())(它必须这样做)。然后,在SuperClass中将a设置为10。
现在我们完成了SuperClass的构造函数,回到SubClass的构造函数,它会将a=20。但是在Java中,字段是不会被覆盖的,所以SuperClass中的a仍然是10。
之后很明显,我们调用c.print(),它调用SubClassprint,再通过super.print()调用SuperClassprint,打印出a,还记得吗?它是10。然后another_print(由于它是private而没有被覆盖)只打印superclass,我们就完成了。

2

我的评论解释了您的代码可能不按预期工作的原因。 下面是您最可能希望它工作的代码。请注意代码中的注释。

static class SuperClass {
    int a; // only declare field in superclass to avoid hiding

    public SuperClass() {
        this.a = 10;
    }

    // make method protected, public, or package private to allow children to override it
    protected void another_print() {
        System.out.println("superclass");
    }

    public void print() {
        System.out.println(this.a);
        this.another_print();
    }
}

static class SubClass extends SuperClass {
    public SubClass() {
        this.a = 20;
    }

    @Override
    protected void another_print() {
        System.out.println("subclass");
    }

    public void print() {
        super.print();
    }
}


public static void main (String[] args) {
    SubClass c = new SubClass();
    c.print();
}

这将打印。
20
subclass

0

我已经调试了我的稍作修改的代码,并发现:

  1. thisSubClass的一个实例
  2. 与Python不同,Java可以使用多个同名变量(正如Peter.Petrov在他的答案中提到的那样,但我没有马上理解)
  3. a中的一个来自SubClass,第二个来自SuperClass(作为隐式超类构造函数调用,这又与Python不同)
  4. this.atest_super()test_sub()中具有不同的值,这就是魔法所在,因为thisSubClass,而Java文档阅读:

this是对当前对象的引用——正在调用其方法或构造函数的对象

我认为我可以接受这个事实,即this将拥有整个依赖树中的所有变量,而Java会根据上下文选择要使用的变量。


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