Casting of Types

  • Choose available object behaviors based on declared type.
  • Express how type casting works for objects.
  • Differentiate between Upcasting and Downcasting.

The advantage of building type hierarchies lies 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 example is a contrived one. However, using the base type Student as in the example above to declare an array could be advantageous, as you have seen in designing the Roster class. On the other hand, there could 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 types:

// 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 other classes. The point is that greetAdvisor uses Student (and GradStudent).

You can send jane or matt to the greetAdvisor method. However, suppose the argument provided to greetAdvisor was not instantiated as GradStudent. In that case, the method will break during its runtime (it will throw ClassCastException to indicate that the code has attempted to cast an object to a sub-class of which it is not an instance of.) 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