8

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.

Idos
  • 15,053
  • 14
  • 60
  • 75
Kirill
  • 444
  • 4
  • 15
  • 2
    not alike. python does not have private methods. – njzk2 Jan 12 '16 at 22:14
  • 7
    You are not `overriding` the super class method since it is private. Also variable a is being hidden since both classes declare it. – George Mulligan Jan 12 '16 at 22:16
  • 1
    You are free to tell your colleagues that Java isn't a pure OOP language either (and that Python probably is, although someone else may know better). – E_net4 Jan 12 '16 at 22:18
  • 3
    it is not about to what `this` refers but how the compiler resolves method invocations and field names. – wero Jan 12 '16 at 22:20
  • 1
    I've gotten in the habit of including the `@Override` annotation in Java, even though it isn't required. Then the compiler will complain when something isn't overwritten properly – OneCricketeer Jan 12 '16 at 22:21
  • see this about what you are doing here: https://en.wikipedia.org/wiki/Variable_shadowing – njzk2 Jan 12 '16 at 22:21
  • "This" is doing the right job. In the super class you are saying use this version. It seems pretty clear from your code, you are calling the print on the super and the super is saying use mine. Plus they are private methods. Try making the them public and take the "this" out of the super print()... – theINtoy Jan 12 '16 at 22:23
  • `private` methods are not dynamically dispatched in Java, hence you cannot `override` them either, only shadow. – Clashsoft Jan 12 '16 at 22:24
  • With reference to properly extending python classes with the `__init__` method: http://stackoverflow.com/a/12701228/2308683 – OneCricketeer Jan 12 '16 at 22:25
  • @njzk2 and others, thanks I've really missed that ``private`` method, just starting Java after Python – Kirill Jan 12 '16 at 22:26

4 Answers4

8

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.

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
8

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.

Idos
  • 15,053
  • 14
  • 60
  • 75
2

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
Community
  • 1
  • 1
George Mulligan
  • 11,813
  • 6
  • 37
  • 50
0

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 as 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.

Community
  • 1
  • 1
Kirill
  • 444
  • 4
  • 15