Casting of Types

The advantage of building type hierarchies lie in the power of type substitution.

Does that mean we should strive to declare objects using more generalized base types? In other words, is one of the following statements preferred to the other?

Student jane = new GradStudent("Jane Doe", "jane@email.com");
GradStudent matt = new GradStudent("Matt Doe", "matt@email.com");

The answer is: it depends (as it usually does) on the problem you are trying to solve.

Before I elaborate more on the answer, let's notice the declaration of jane is similar to the case where we have an array of students and add objects of type GradeStudent to it:

Student[] students = new Student[10];
students[0] = new GradStudent("Jane Doe", "jane@email.com");

This is not a contrived example; as you have seen in the case of designing the Roster class, using the base type Student is advantageous (for the reasons discussed in the previous section). In other cases, it may be disadvantageous to declare objects to their base type. For example, if we need to access the advisors of graduate students, we would have difficulty with jane:

matt.getAdvisor(); // works fine
jane.getAdvisor(); // Compiler error: Cannot resolve method 'getAdvisor' in 'Student'

Since jane (the object) is declared as a Student (and not a GradStudent) its behavior (i.e. what it does, what operations/methods can be invoked on it) is limited to those declared in the Student class.

It is noteworthy to point out jane has advisor (since she is a GradStudent); you can get that information "out of jane" by casting:

// Assumption: s is instantiated as GradStudent
private static String greetAdvisor(Student s) {
  // Cast object of Student to GradStudent
  GradStudent gs = (GradStudent) s;
  // Call getAdvisor() on the casted object
  return "Hi, " + gs.getAdvisor();
}
Aside: Which class the greetAdvisor method is being defined in?

It doesn't matter! The greetAdvisor is not defined in Student nor GradStudent; it is defined in some other class. The point is that greetAdvisor uses Student (and GradStudent).

You can send jane or matt to the greetAdvisor method. However, if the argument provided to greetAdvisor was not instantiated as GradStudent, the method will break during its runtime (it will throw ClassCastException to indicate that the code has attempted to cast an object to a subclass of which it is not an instance.) To guard against this scenario you can rewrite greetAdvisor as follows:

private static String greetAdvisor(Student s) {
  // Use instanceof operator to check 
  // if the object belongs to a specific type
  if (s instanceof GradStudent) {
    GradStudent gs = (GradStudent) s;
    return "Hi, " + gs.getAdvisor();
  } else {
    return "This student has no advisor!";
  }
}

Casting from Student to GradStudent is known as Downcasting, the typecasting of a parent object to a child object.

When an object of type GradStudent is passed as an argument to greetAdvisor, the compiler implicitly casts it to Student. The typecasting of a child object to a parent object is known as Upcasting.

Resources