Rofgar
Rofgar

Reputation: 358

Java Inheritance Base Class uses Derived Class Method

Recently I stumbled upon Java tests and found a strange behaviour.

class MainOne {
    
    private int i = 5;
    
    public MainOne() {
        System.out.println("MainOne says that I is " + getI());
    }
    
    public int getI() {
        System.out.println("MainOne getI was used");
        return i;
    }
    
}

public class TheTest extends MainOne {

    private static int i = 10;
    
    public TheTest() {
        System.out.println("TheTest says that I is " + super.getI());
    }
    
    
    public int getI() {
        System.out.println("TheTest getI was used");
        return i;
    }
    
    public static void main(String[] args) {
        TheTest test = new TheTest();
    }
    
}

Result was:

TheTest getI was used

MainOne says that I is 10.

MainOne getI was used

TheTest says that I is 5

Question is, what happened? How come Base class uses it descendant's method?

Upvotes: 4

Views: 4846

Answers (2)

lkamal
lkamal

Reputation: 3938

There are two important points to note in this problem.

  1. super.getI() invocation inside MainOne

    public TheTest() {
        System.out.println("TheTest says that I is " + super.getI());
    }
    

    forcefully this invocation goes to super class even though the method is overridden.

  2. Field i in subclass is defined as a static field

    There is a difference in the results based on whether this field is a static or not. When the overridden getI() is invoked from TheTest constructor, the invocation goes to the MainOne classes method. However when this invocation happens, instance fields of TheTest class is not yet initialized to the assigned values (but only default values).

    If the field i in TheTest is a instance field, getI() will print 0 instead of 10.

    Details of the sequence of invocation is hard to represent here. However in case you wanted to find further details, please refer below.

    new TheTest()
        -> super() - implicitly invoke default constructor
            -> inside MainOne() constructor
                -> System.out.println("MainOne says that I is " + getI());
                    -> this results in a getI() invocation
                    -> since getI() is overridden by subclass
                    -> invoke on TheTest instance, not MainOne class.
                    -> TheTest's instance fields not given the assigned values
                    -> TheTest's static fields have got the assigned values
                <- string concatenation completes for System.out.println()
            <- MainOne constructor (or the super) is completed
        <- super() completes
    
        -> System.out.println("TheTest says that I is " + super.getI());
            -> this results in a getI() invocation
            -> however explicit super.getI() forcefully goes to super class
            -> invoke on MainOne (super) instance, not TheTest class.
        <- string concatenation completes for System.out.println()
    <- new TheTest() constructor completes
    

Hope this helps you in understanding the details further.

Upvotes: 0

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279910

This is all about order of execution and constructor inheritance. TheTest constructor implicitly calls the super MainOne constructor.

so

public TheTest() {
    System.out.println("TheTest says that I is " + super.getI());
}

calls

public MainOne() {
    System.out.println("MainOne says that I is " + getI());
}

which calls the overriden getI() because of polymorphism.

public int getI() {
    System.out.println("TheTest getI was used");
    return i;
}

The i here is the static i declared in TheTest. Finally

super.getI());

gets called which uses MainOne's i.

You therefore get

TheTest getI was used

MainOne says that I is 10.

MainOne getI was used

TheTest says that I is 5

Note that polymorphism does not apply to fields and that fields (whether static or instance) may hide fields of the same name in parent classes.

Upvotes: 6

Related Questions