rpg
rpg

Reputation: 1

java - Overriding with covariant types wrt variables

class G {

    int x = 5;
}

class H extends G {

    int x = 6;
}

public class CovariantTest {

    public G getObject() {
        System.out.println("g");
        return new G();
    }

    public static void main(String[] args) {
        CovariantTest c1 = new SubCovariantTest();
        System.out.println(c1.getObject().x);
        System.out.println(new H().x);
    }
}

class SubCovariantTest extends CovariantTest {

    public H getObject() {
        System.out.println("h");
        return new H();
    }
}

Output:

h
5
6

Apparently the two println statements in the main method aren't the same. How is the new H() object returned from class SubCovariant's getObject method assigned to a G reference?

Upvotes: 0

Views: 144

Answers (2)

Peter Lawrey
Peter Lawrey

Reputation: 533530

When you override a method, it is the type of the instance which matters, not the type of the reference. This is how polymorphism works.

CovariantTest c1 = new SubCovariantTest();

This converts the type of the reference, but not the implementation. if you were to do

System.out.println(c1.getClass());

this would print

SubCovariantTest

so when you call getObject() on this instances, it should be no surprise that this calls SubCovariantTest.getObject()


By comparison, static methods don't follow polymorphism. They cannot be overridden in the same way (they can only be hidden) If you were to make getObject() static in both cases you would find that c1.getObject() would call the matching the type of c1 as the method to call is determined at compile time, not runtime. In fact you can do this.

public class CovariantTest {

    public G static getObject() {
        System.out.println("g");
        return new G();
    }

    public static void main(String[] args) {
        CovariantTest c1 = null;
        System.out.println(c1.getObject().x); // prints "g" "5"
    }
}

class SubCovariantTest extends CovariantTest {

    public H static getObject() {
        System.out.println("h");
        return new H();
    }
}

You can access a null reference here because it is not used at runtime. The compiler only uses the type of the reference, as you might have expected in your question.

Upvotes: 1

Steve Chaloner
Steve Chaloner

Reputation: 8202

G is the declared type, H is the actual type.

The declared type is what you can think of the object as, i.e. G in your example.

The actual type is what the object actually is, i.e. H in your example. This provides actual behaviour, including any which it might inherit from parent classes including G.

Upvotes: 1

Related Questions