Sobrecarga de operadores en Kotlin. Añade operaciones estándar a cualquier clase (KDA 17)

En Kotlin, como en todo lenguaje, tenemos operadores predefinidos para realizar ciertas operaciones.
Los más típicos son la suma (+), resta (-), multiplicación (*) o división (/), pero hay unos cuantos más.
En algunos lenguajes, como en Java, estos operadores están limitados a ciertos tipos de datos, y no tenemos forma de hacer que otros tipos los utilicen.
Hay otros lenguajes como Scala en los que nos podemos inventar cualquier tipo de operador que se nos ocurra, ya que los nombres de las funciones aceptan cualquier símbolo.
En Kotlin se toma una solución intermedia: hay una serie de operadores predefinidos, pero los podemos sobrecargar para cualquier tipo de datos.
¿Te gustaría comenzar hoy a dar el siguiente paso? Te recomiendo que entres en mi training gratuito aquí.
Sobrecarga de operadores en Kotlin
Como hablábamos, en Kotlin se pueden sobrecargar cierto número de operadores, implementando la función correspondiente en nuestra clase. Esa función debe ser marcada con la palabra reservada operator
.
Los operadores son básicamente los siguientes:
Operadores Unarios
+a | a.unaryPlus() |
---|---|
-a | a.unaryMinus() |
!a | a.not() |
a++ | a.inc() |
a— | a.dec() |
Operadores Binarios
a + b | a.plus(b) |
---|---|
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.mod(b) |
a..b | a.rangeTo(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.modAssign(b) |
Operadores tipo array
a[i] | a.get(i) |
---|---|
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
Operación Equals
a == b | a?.equals(b) ?: b === null |
---|---|
a != b | !(a?.equals(b) ?: b === null) |
Las operaciones equals
son un poco diferentes, porque usan una traducción más compleja para hacer un chequeo correcto, y porque esperan una especificación exacta de la función, y no sólo un nombre específico para la misma. La función debe ser implementada exactamente así:
fun equals(other: Any?): Boolean
Invocación de funciones
a(i) | a.invoke(i) |
---|---|
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
Un ejemplo
Imagina que tienes un modelo de datos que son compañías, cada una de las cuales tiene un listado de empleados.
Podrías utilizar el operador get
para acceder a las posiciones mediante corchetes. La implementación es muy sencilla:
class Employee(val id: Long, val name: String)
class Company(private val employees: List) {
operator fun get(pos: Int) = employees[pos]
}
Y así es como podrías usarlo:
val company = Company(listOf(Employee(1235, "John"), Employee(2584, "Mike")))
val mike = company[1]
Pero podrías ir más, allá, y usar el id para recuperar el valor, implementando la función así:
operator fun get(id: Long) = employees.first { it.id == id }
val mike = company[2584]
Conclusión
Los operadores nos pueden ayudar a dar mayor legibilidad utilizando símbolos comunes para operaciones conocidas.
Hay que tener cuidado con no utilizarlos en situaciones en las que pueda llevar a confusión a las personas que lo lean, pero se pueden convertir en una herramienta realmente interesante.
Si todo esto te apasiona tanto como a mí, te animo a que te apuntes a mi training gratuito donde te contaré todo lo que necesitas para aprender a crear tus Apps Android en Kotlin desde cero.
Cómo conseguir la localización amplia en Android
Cómo pedir permisos en Jetpack Compose