Casting an instance of a base class to a subclass as in : b = (B) a;
is called narrowing (as you are trying to narrow the base class object to a more specific class object) and needs an explicit type-cast.
Casting an instance of a subclass to a base class as in: A a = b;
is called widening and does not need a type-cast.
To illustrate, consider the following class declarations, and test code:
class Vehicle {
}
class Car extends Vehicle {
}
class Truck extends Vehicle {
}
class MotorCycle extends Vehicle {
}
class Test {
public static void main(String[] args) {
Vehicle vehicle = new Car();
Car car = new Car();
vehicle = car; // is valid, no cast needed
Car c = vehicle // not valid
Car c = (Car) vehicle; //valid
}
}
The statement Vehicle vehicle = new Car();
is a valid Java statement. Every instance of Car
is also a Vehicle
. Therefore, the assignment is legal without the need for an explicit type-cast.
On the other hand, Car c = vehicle;
is not valid. The static type of the vehicle
variable is Vehicle
which means that it could refer to an instance of Car
, Truck,
MotorCycle, or any other current or future subclass of
Vehicle. (Or indeed, an instance of
Vehicleitself, since we did not declare it as an
abstractclass.) The assignment cannot be allowed, since that might lead to
carreferring to a
Truck` instance.
To prevent this situation, we need to add an explicit type-cast:
Car c = (Car) vehicle;
The type-cast tells the compiler that we expect the value of vehicle
to be a Car
or a subclass of Car
. If necessary, compiler will insert code to perform a run-time type check. If the check fails, then a ClassCastException
will be thrown when the code is executed.
Note that not all type-casts are valid. For example:
String s = (String) vehicle; // not valid
The Java compiler knows that an instance that is type compatible with Vehicle
cannot ever be type compatible with String
. The type-cast could never succeed, and the JLS mandates that this gives in a compilation error.