Java Reference
In-Depth Information
Assigning a superclass reference to a subclass variable is called downcasting (or narrowing conversion).
Downcasting is the opposite of upcasting. In upcasting, the assignment moves up the class hierarchy whereas in
downcasting the assignment moves down the class hierarchy. The Java compiler cannot make sure at compile time
that downcasting is legal or not. Let's consider the following snippet of code:
Employee emp;
Manager mgr = new Manager();
emp = mgr; // Ok. Upcasting
mgr = emp; // A compiler error. Downcasting
The assignment emp = mgr is allowed because of upcasting. However, the assignment mgr = emp is not allowed
because it is a case of downcasting where a variable of superclass ( Employee) is being assigned to a variable of
subclass ( Manager) . The compiler is right in assuming that every manager is an employee (upcasting). However, not
every employee is a manager (downcasting). In the above snippet of code, you would like the downcasting to work
because you know for sure that the emp variable holds a reference to a Manager . Java imposes an additional rule in
order for your downcast to succeed at compile-time. You need to give additional assurance to the compiler that you
have considered the assignment of a superclass reference to a subclass reference variable and you would like the
compiler to pass it. You give this assurance by adding a typecast to the assignment, as shown:
mgr = (Manager)emp; // OK. Downcast at work
The above downcasting with a typecast succeeds at compile-time. However, the Java runtime will perform an
additional verification. The job of the compiler is just to make sure that the declared type of mgr variable, which is
Manager , is assignment compatible with the typecast being used, which is Manager . The compiler cannot check what
type of object emp variable will actually refer to at runtime. The Java runtime verifies the correctness of the typecast
(Manager)emp in the above statement.
The type of the object to which the emp variable refers at runtime is also called its runtime type. The runtime
compares the runtime type of the emp variable and the Manager type ( Manager type is used in the typecast). If the
runtime type of emp variable is assignment compatible with the type used in typecast, the typecast succeeds at
runtime. Otherwise, runtime throws a java.lang.ClassCastException .
Let's consider the following snippet of code assuming that you have a subclass of the Manager class, which is
called PartTimeManager .
Employee emp;
Manager mgr = new Manager();
PartTimeManager ptm = new PartTimeManager();
emp = mgr; // Upcasting. OK
ptm = (PartTimeManager)emp; // Downcasting. OK at compile-time. A runtime error.
The last assignment, which uses downcasting, succeeds at compile-time because the declared type of the ptm
variable and the typecast type are the same. The runtime type of emp is Manager , because the emp = mgr statement
assigns a Manager object's reference to it. When the runtime attempts to execute the “ (PartTimeManager emp) ”part
of the downcasting, it finds that the runtime type of emp , which is Manager , is not assignment compatible with the
typecast type, which is PartTimeManager . This is the reason that the runtime will throw a ClassCastException .
You can think of a statement that involves a downcasting as having two parts for the verification purpose.
Suppose the statement is a2 = (K)b2 . The compiler's job is to verify that the declared type of a2 is assignment
compatible with type K . The runtime's job is to verify that the runtime type of b2 is assignment compatible with type K .
If any of the two checks fails, you get an error at compile-time or runtime depending of which check fails. Figure 16-2
depicts this scenario.
 
Search WWH ::




Custom Search