Type Checking & Conversion
You have probably used casting to convert numeric values from one type to another, i.e.:
double a = 5.5;
int b = (int) a;
a: float = 5.5
b: int = int(a)
What you are actually doing when you cast is transforming a value from one type to another. In the first case, you are taking the value of a
, which is the floating-point value 5.5
, and converting it to the equivalent integer value 5
.
Both of these are examples of an explicit cast, since we are explicitly stating the type that we’d like to convert our existing value to.
In some languages, we can also perform an implicit cast. This is where the compiler or interpreter changes the type of our value behind the scenes for us.
int a = 5;
double b = a + 2.5;
a: int = 5
b: float = a + 2.5;
In these examples, the integer value stored in a
is implicitly converted to the floating point value 5.0
before it is added to 2.5
to get the final result. This conversion is done automatically for us.
However, as we’ve observed already, each language has some special cases where implicit casting is not allowed. In general, if the implicit cast will result in loss of data, such as when a floating-point value is converted to an integer, we must use an explicit cast instead.
Casting and Inheritance
Casting becomes a bit more involved when we consider inheritance. As you saw in the previous discussion of inheritance, we can treat derived classes as the base class. For example, in Java, the code:
Student willie = new UndergraduateStudent("Willie", "Wildcat");
is actually implicitly casting the UndergraduateStudent
object “Willie Wildcat” into a Student
class. Because an UndergraduateStudent
is a Student
, this cast can be implicit. Going the other way requires an explicit cast as there is a chance that the Student
we are casting isn’t an UndergraduateStudent
, i.e.:
UndergraduateStudent u = (UndergraduateStudent)willie;
If we tried to cast willie
into a graduate student:
GraduateStudent g = (GraduateStudent)willie;
The program would throw a ClassCastException
when run.
In Python, things are a bit different. Recall that Python is a dynamically typed language. So, when we create an UndergraduateStudent
object, the Python interpreter knows that that object has the type UndergraduateStudent
. So, we can treat it as an instance of both the Student
and UndergraduateStudent
class. We don’t have to perform any conversions to do so.
However, if we try to treat it like an instance of the GraduateStudent
class, it would fail with an AttributeError
.
Checking Types
Both Java and Python include special methods for determining if a particular object is compatible with a certain type.
Student u = new UndergraduateStudent("Willie", "Wildcat");
if (u instanceof UndergraduateStudent) {
UndergraduateStudent uGrad = (UndergraduateStudent) willie;
// treat willie as an undergraduate student here
}
u: Student = UndergraduateStudent("Willie", "Wildcat")
if isinstance(u, UndergraduateStudent):
# treat willie as an undergraduate student here
Java uses the instanceof
operator to perform the check, while Python has a built-in isinstance
method to perform the same task. Typically these statements are used as part of a conditional statement, allowing us to check if an object is compatible with a given type before we try to use that object in that way.
So, if we have a list of Student
objects, we can use this method to determine if those objects are instances of UndergraduateStudent
or GraduateStudent
. It’s pretty handy!