2

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;
        }
    }
}
Aditya Verma
  • 177
  • 8
  • `super` keyword, just like `this` keyword is based on *instance* type. Via `new Third()` you created instance of `Third`, so `super` for that instance will "represent" `Second` type. So you called `getClass()` 3 times trying to invoke code available for `Second` class which is *same* implementation (it inherited from `Object`) which is why you are seeing same results. – Pshemo Sep 06 '18 at 18:28
  • When `getClass` is called, it's called on the single object that you created, which is of type `Third`. Each of those constructors is actually run, but since they're called on the same object, they print the same thing. To see more clearly what is happening, try adding `System.out.println("first"")` to the `First` class, and similar for the other two classes. – AsherMaximum Sep 06 '18 at 18:51
  • 1
    @Aditya Thank you for the compliment! I added a little to my answer to demonstrate how `super.equals` works in your actual example. Please don't hesitate to include cases like your example from the beginning, as they can help give answerers a better idea of what you're looking for. Cheers! – Jeff Bowman Sep 07 '18 at 00:11

2 Answers2

4

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.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
3

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