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.