Lesson 6 / 25

Type Checks, Casts and the Type Hierarchy

is, as, as? and Any, Unit, Nothing.

Checking and converting types

x is Type checks a type and smart casts x in the guarded scope; !is is the negation. x as Type is an unsafe cast that throws ClassCastException on failure, while x as? Type is a safe cast returning null. At the top of the hierarchy is Any (and Any? for nullable values). Functions with no useful return value return Unit. Nothing is the type of expressions that never complete normally, such as throw or TODO(), which is why ?: throw ... type-checks. Numbers do not convert implicitly: use toLong(), toDouble() and similar.

Safe casts and explicit conversion

No implicit widening.

fun lengthOf(value: Any): Int {
    if (value is String) return value.length          // smart cast
    val list = value as? List<*> ?: return -1          // safe cast + elvis
    return list.size
}

fun fail(message: String): Nothing = throw IllegalStateException(message)

fun main() {
    val small: Int = 42
    val big: Long = small.toLong()       // explicit conversion required
    val ratio = small / 5.0              // Int / Double gives Double
    val config: String = System.getenv("APP_MODE") ?: fail("APP_MODE not set")
}

Prefer is over as

An is check followed by a smart cast is safer and clearer than an unchecked as. Reach for as? when a failed cast is a normal outcome.

Quick check: What does `value as? String` return if `value` is an Int?

  • An empty string
  • It throws ClassCastException
  • The Int converted to text
  • null
Answer

null — as? is the safe cast.