Type Casting Automático en Kotlin

Type Casting Automático en Kotlin

Photo by Suzanne D. Williams on Unsplash

En el primer episodio de TGIK, vimos una introducción al mundo de Kotlin e incluso logramos explicar características básicas de Kotlin que hacen que escribirlo sea divertido y fácil de leer. 🤓

Una de esas características fue el type casting automático, y en esta oportunidad veremos más a detalle como está característica es posible.

Si no han logrado ver este episodio, les comporta el enlace: 👀

TGIK — episodes[0] = “Kotlin Starter Pack”

El type casting automático es una de las muchas características que hacen a Kotlin un lenguaje sensacional. Si son curiosos como yo, tal vez querrán saber como el compilador de Kotlin hace que el type casting automático nos ayude a tener un código más conciso.

Type Casting

Kotlin es un lenguaje de programación de tipado estático, lo que quiere decir que la comprobación de tipos se realiza durante la compilación, y no durante la ejecución.

Pero, ¿a qué me refiero con tipos? Kotlin fue diseñado para poder usarse con el paradigma de programación orientada a objetos. En este paradigma cada objeto que se define es una instancia de una clase, o en otras palabras, cada objeto tiene un tipo de dato asociado.

El Operador is

Tenemos que mencionar al operador que hace el type casting automático sea posible. Me refiero al operador is. Este operador revisa si una expresión es una instancia de un tipo.

fun main() {
    val obj: Any = "Kotlin!"
    if (obj is String) {
        println("obj es un String")
    }
}

También existe la contraparte !is que es equivalente a escribir !(obj is String).

Type Casting… Automático

Lo realmente genial del operador is es cuando se revisa una variable local o propiedad que es inmutable. En este caso no hay necesidad de convertirla explícitamente. Creo que queda más claro si lo vemos con código:

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` se convierte automaticamente a `String` en este bloque
        return obj.length
    }

    // `obj` aun es de tipo `Any` afuera del cuerpo del `if`
    return null
}

Y eso no es todo. El inteligente compilador de Kotlin sabe convertir automáticamente entre datos cuando se usan los operadores && o || en condiciones. También funciona en expresiones when y ciclos while. Veamos lo en código:

fun main() {
    val obj: Any = "TGIK!"

    // obj es convertido automaticamente a String en el lado derecho de `||`
    if (obj !is String || obj.length == 0) return

    // obj es convertido automaticamente a String en el lado derecho de `&&`
    if (obj is String && obj.length > 0) {
        println(obj.length) // obj es convertido automaticamente a String
    }

    when (obj) {
        is Int -> print(obj + 1)
        is String -> print(obj.length + 1)
        is IntArray -> print(obj.sum())
    }
}

Type casting automático tras bambalinas

En está ocasión nos interesa saber que está pasando tras bambalinas, y precisamente veremos que hace el compilador de Kotlin para que esto sea posible.

Usaremos este ejemplo que podemos obtener de la documentación de Kotlin:

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` is automatically cast to `String` in this branch
        return obj.length
    }

    // `obj` is still of type `Any` outside of the type-checked branch
    return null
}

Cuando usamos Kotlin con la Java Virtual Machine, el compilador convierte el código de Kotlin a bytecode que la JVM puede luego usar. En este ejemplo no veremos bytecode sino de la ayuda de IntelliJ descompilaremos el bytecode a código Java.

Este seria el código descompilado a Java del ejemplo anterior:

public final class AutomaticCasts {
   @Nullable
   public static final Integer getStringLength(@NotNull Object obj) {
      Intrinsics.checkParameterIsNotNull(obj, "obj");
      return obj instanceof String && ((String)obj).length() > 0 ? ((String)obj).length() : null;
   }
}

Lo que podemos observar es justamente el código que tendríamos que escribir si no existiera una característica como casts automáticos, el cual es el caso de Java. Cada vez que intentamos acceder a un método de la clase String tenemos que hacer cast del objeto a String explícitamente. Podemos ver como una característica tan sencilla hace que nuestro código sea más conciso y fácil de entender.

¿Qué pasa con otras plataformas?

Pero Kotlin no solo corre en la JVM, también puede correr en otras plataformas. Así que para finalizar veremos que sucede cuando sea usa Kotlin en el frontend con JavaScript. El resultado es casi el mismo ya que JavaScript es un lenguaje débilmente tipado y dinámico, y no necesita convertir entre tipos de datos.

Aquí podemos ver el código de JavaScript al cual es traducido el ejemplo anterior de Kotlin:

function getStringLength(obj) {
    if (typeof obj === 'string' && obj.length > 0) {
        return obj.length;
    }
    return null;
}

Muy parecido no?

Conclusiones

Vimos más a detalle una característica sencilla que posee Kotlin como son los casts automáticos. Pudimos observar que está característica en realidad hace que el código se vea más conciso y quita mucho “overhead” que se tendría que escribir si no se tuviera como es el caso de Java.

Si quieren aprender más de Kotlin, pueden vernos en el canal de Guatemala Kotlin User Group por YouTube y seguirnos en la cuenta de Twitter. Todos los viernes tendremos en vivos en donde hablaremos de varios temas que hacen a Kotlin un gran lenguaje de programación. 🥳