Prev Next

Casting Objects

There is yet another technical nitty-gritty when dealing with super and sub class instances. For example, consider the distance() method of Point class which takes a Point instance as an argument and computes the distance between them. If we are to override this method in Point3D class, it obviously will take a Point3D type instance as an argument.

        // Point.java
        public int distance(Point q) {
            ..... 
        }
      
        // Point3D.java
        public int distance(Point3D q3) {
            ..... 
        }
      

Looking closely, can you really say the super class method is overriden by the sub class one? Apparently, they don't have the same signature. The former takes Point type as the argument while the latter takes Point3D as the argument.

Although Point and Point3D are different, they are related in an hierarchical manner. So, it is not completely correct to say they are of different types. But how do we reconcile?

A sub class is more specific compared to a super class. To get a better clarity, consider two classes: Animal and Dog. The Animal class is general while the Dog is specific. Dog inherits the features of Animal class. We can say every dog is an animal but not the vice-versa. This kind of gives a clue as to how we should reconcile. It all boils down to what is valid assignment/casting and what isn't.

1. You can assign a variable of sub class type to a variable of super class type. But not the vice-versa.

  Point p = new Point3D();    // Valid assignment
  Point3D p3 = new Point();   // Invalid assignment

2. You can pass an argument of sub class type when the expected argument of the method is of super class type. But not the vice-versa. So, when you have a hierarchy of classes, always specify the argument of the overriden method as that of super class type instead of sub class type.

Preferred Not preferred
        // Point3D.java
        public int distance(Point q) {
            ..... 
        }
      
        // Point3D.java
        public int distance(Point3D q3) {
            ..... 
        }
      
Why preferred? Why isn't preferred?
        // Driver.java
        Point3D p3 = new Point3D(3,-8,4);
        Point3D q3 = new Point3D(1,-1,6);
        Point r = new Point(-7,1);

        int d1 = p3.distance(q3); // Valid
        int d2 = p3.distance(r);  // Valid
      
        // Driver.java
        Point3D p3 = new Point3D(3,-8,4);
        Point3D q3 = new Point3D(1,-1,6);
        Point r = new Point(-7,1);

        int d1 = p3.distance(q3); // Valid
        int d2 = p3.distance(r);  // Invalid
      

3. This applies to return type also. So, when you have a hierarchy of classes, always specify the return type of the overriden method to that of sub class instead of super class (reverse of what you do with argument type).

  // Point3D.java
  public Point3D sum(Point q) {   // return type is Point3D, not Point
      .......
  }

This will enable the return value to be assigned to both Point and Point3D types.

  // Driver.java
  Point3D r3 = p3.sum(q3);
  Point s = p3.sum(q3); // Implicit casting happens
  Point r = p.sum(q);
  Point3D s3 = (Point3D) p.sum(q);  // Explicit casting is necessary

An easier way to remember

Super class: Animal     Sub class: Dog

Animal a = x;    // valid if x is an instance of Animal or Dog
Dog d = x;       // valid only if x is an instance of Dog

Animal a = someObject.someMethod();    // valid if someMethod() returns Animal or Dog
Dog d = someObject.someMethod();     // valid only if someMethod() returns Dog

someObject.someMethod(Animal a);    // valid if called with Animal or Dog instance
someObject.someMethod(Dog d);     // valid only if called with Dog instance

Exercises

Override distance(), scalarMultiply(), sum(), equals() and print() methods in Point3D with appropriate argument and return types. Test them by making appropriate assignment. Get comfortable with inheritance, overriding and casting concepts.

Note: Methods to compute slope and orientations don't make sense in 3-dimensional space. Hence, they don't need to be supported.

A Note on Multiple Inheritance

Extending multiple classes simultaneously is not possible in Java. For example if A and B are two classes, we cannot do as follows:

  
     public class C extends A, B {
              :
     }

C++ supports this feature. However, it is prone to be misused if not understood properly and can lead to all kinds of problems that are difficult to debug. So Java has avoided this feature. You can extend only one class at a time.