Type Checking and Casting

Sometimes you may wonder if the object at hand is of a particular type you expect. And, once you verify, you often cast the reference to that type so you can invoke the methods you desire—well, at least, that’s the case in Java. Here we’ll see how Kotlin supports these two operations of type checking and type casting. Along the way you’ll see how to benefit from Kotlin’s type safety, and at the same time see how the language removes your need to write mundane code.

Type checking

“Is it a feature or a flaw?” is an unsettled debate about runtime type checking. It’s necessary to check an object’s type occasionally, but from the extensibility point of view, we should use it sparingly. Checking for arbitrary types can make the code brittle when new types are added and lead to failure of the Open-Closed Principle—see Agile Software Development, Principles, Patterns, and PracticesRobert C. Martin. Agile Software Development, Principles, Patterns, and Practices. Prentice Hall, Englewood Cliffs, NJ, 2002. Think many times before writing code to check for runtime types.

Having said that, checking for runtime type is useful and unavoidable in two situations. One is in the implementation of the equals() method—we need to know if the instance at hand is of the current class. The other is within when, if the path to take depends on the type of an instance.

Let’s first look at how to check for the runtime type, and then at a nice little feature that removes the burden of casting once the type is confirmed.

Using is

The equals() method of Object performs reference-based comparison, but classes may override that method to determine equality. Continuing that tradition, all Kotlin classes, which extend from the Any class (discussed in Any, the Base Class), may override the equals() method. We also saw in Improved Equality Check, that Kotlin maps the == operator to the equals() method. Even though we’ve not looked at classes in Kotlin yet, let’s move forward to implement the equals() method in a class for the sake of exploring type checking.

In the code below, an Animal class overrides the equals() method with an implementation that wants to treat all Animal instances as being equal. But if the parameter given—which has to be of type Any? due to the signature of the method being overridden from Any—is not an instance of Animal, it wants to return false.

// equals.kts
class Animal {
   override operator fun equals(other: Any?) = other is Animal
}

The is operator is used to check if the object pointed to by a reference, other in this example, is of a particular type, Animal in this case. If the instance is of the expected type, then the result of that expression is true; otherwise it’s false. Let’s use that equals() method in an example:

Get hands-on with 1200+ tech skills courses.