Aditya Verma
Aditya Verma

Reputation: 177

Java: Calling super.getClass() from subclass

I know this question has been already asked here, but I fail to understand the "Why" part.

Let us take the following example:

public class First {
    First() {
       System.out.println(super.getClass());
    }
}

public class Second extends First {
    Second() {
       System.out.println(super.getClass());
    }
}

public class Third extends Second {
    Third() {
       System.out.println(super.getClass());
    }
}  

When I instantiate an Object of type Third:

public class Main {
    public static void main(String[] args) {
        Third third = new Third();
    }
}

The output is:

class Third
class Third
class Third  

And what I expected was (Thinking that super.getClass() should return the name of parent class):

class java.lang.Object
class First
class Second

Which shows that I don't understand how does Inheritance actually work in Java. Kindly help me in getting the right concept in my head.

EDIT

My actual intention was to understand how inheritance actually works (which has been explained very well by Jeff), instead of getting the expected output.
This doubt arose when I was trying to the understand why the following code worked (More specifically, why does super.equals(point3d) worked as it has been passed an object of type Point3D)

public class Main {
    public static void main(String[] args) {
        Point3D p1 = new Point3D(1, 2, 3);
        Point3D p2 = new Point3D(1, 2, 3);
        System.out.println(p1.equals(p2));  // Output: true
    }
}

public class Point {
    private int x;
    private int y;

    public Point() {
        this.x = 0;
        this.y = 0;
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object object) {
        if (object != null && object.getClass() == this.getClass()) {
            Point point = (Point) object;
            return point.x == this.x && point.y == this.y;
        } else {
            return false;
        }
    }
}

public class Point3D extends Point {
    private int z;

    public Point3D() {
        this.z = 0;
    }

    public Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public boolean equals(Object object) {
        if (object != null && object.getClass() == this.getClass()) {
            Point3D point3D = (Point3D) object;
            return super.equals(point3D) && point3D.z == this.z;  // Had doubt here
        } else {
            return false;
        }
    }
}

Upvotes: 2

Views: 331

Answers (2)

Jeff Bowman
Jeff Bowman

Reputation: 95764

It is important to recognize that there is exactly one object here with one reference. It can be tempting to present it as though the superclass is a separate instance of Second residing within your instance of Third, but it is not true; there is no way to refer to that instance because it does not exist.

To be clear: It is not the case that within Third there is a hidden instance of Second to which super refers, and within Second there is a First, and within First there is an Object. Instead, there is a single object that is able to behave as an Object, First, Second, or Third. Regardless of the type of the local variable or reference ("static type"), the instance itself has a "runtime type", which is Third.

The only thing super can do for you is to deliberately invoke a member that belongs to a superclass (JLS 15.11.2) that may be hidden by overriding or naming. This does nothing here, because getClass() is a final method declared on Object. getClass has documentation that it "Returns the runtime class of this Object" (docs). There can be no varying implementation, so you will always receive the type Third as you do in your question.


UPDATE: Unlike getClass, equals is non-final, and can be overridden. Point.equals ensures that the Class returned by getClass is equal, and that x and y are equal. Rather than writing an entirely different implementation of equals, Point3D defers to the Point's definition of equals and additionally checks that the z field is equal, which works because Point checks that object.getClass() == this.getClass(), not that object.getClass() == Point.class. It could not do this simply by calling equals, because that would use the Point3D.equals implementation; instead it has to call super.equals to see how Point would calculate equals.

However, I hope this is an example in a lesson, because polymorphism asserts that a Point3D is-a Point and can do whatever a Point can do (see the Liskov Substitution Principle). For Point and Point3D, this could be misleading: You could write a method double distanceBetween(Point a, Point b) that works as expected using 2D points but gives an incorrect result when using 3D points. In real environments, you'll need to be careful about what your class hierarchies and what they imply.

Upvotes: 4

getClass() is a method on Object. There's not a separate one for each of your subclasses. If you want to go up the chain like that, call getSuperclass() on the result of getClass().

Upvotes: 3

Related Questions