简体   繁体   中英

What does Java `this` actually refer to in an inheritance situation?

Why does the following Java code produces:

10
superclass

The code in question is:

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

There is no instance of SuperClass ever created, isn't there? Not only that Java starts looking for the method to invoke from the SuperClass , it even somehow knows that a = 10 !

Let's consider a similar Python code:

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()

It works as I expect:

20
subclass

The only explanation that my colleagues (Python disliking Java folks) came up with is: "Python is not a true OOP language". Not very convincing at all.

Update: private void another_print() is my blunder, I should have used protected .

In the sub-class's print you just call super-class's print method. So it prints the a from the super class of course.

You have two separate a fields here. Fields are not subject to overriding, only methods are. The super-class has an a field and you have another a field in the sub-class.

If another language produces another result, that's not a big surprise. Also, I am not sure your Python code is logically equivalent/analogous to your Java code.

It is the order of constructor calling in Java.
In the SubClass , when you instantiate c , the constructor implicitly calls the default constructor of the SuperClass ( public SuperClass() ) (it must do so). Then a is set to be 10 in the SuperClass .

Now that we're done with the SuperClass constructor, we get back to the constructor of SubClass , which assigns a = 20 . But fields are not subject to overriding in java, so a in SuperClass is still 10.

After that it's pretty obvious, we call c.print() which calls the print of SubClass , which calls the print of SuperClass (by super.print() ), which prints a which is as you remember 10. Then another_print (which is not overridden since it is private ) just prints superclass and we're done.

My comment explained the reason your code probably doesn't work as expected. Below is code written how you most likely expected it to work. Note the comments in the code.

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

This will print

20
subclass

I've debugged my slightly corrected code and found out that:

  1. this is an instance of SubClass
  2. Unlike Python, Java is ok with more than one variable of the same name (as peter.petrov mentioned in his answer , but I didn't got it right away)
  3. One of the a s is from the SubClass and the second is from the SuperClass (as implicit superclass constructor call, again unlike Python)
  4. this.a has a different value in test_super() and test_sub() and that is the magic, given that this is a SubClass and Java documentation reads:

this is a reference to the current object — the object whose method or constructor is being called

I think I can live with the fact that this will have all the variables from the whole dependency tree and Java will select which one to use depending on the context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM