Las clases en Kotlin están simplificadas para escribir la mayor cantidad de funcionalidades con la menor cantidad de código. Las clases son la forma en la que definimos objetos del mundo real en nuestro código. Su sintaxis es simple o, en el peor de los casos, similar a la de otros lenguajes como Java. Kotlin sigue el mismo paradigma de orientado a objetos y cuenta con los mismos conceptos de constructores, herencia, interfaces y clases abstractas que en Java, aunque con una diferencia sustancial en el manejo de las propiedades. Una diferencia importante es que en Kotlin no es necesario definir explícitamente los métodos Get y Set, ya que el lenguaje los genera automáticamente.
Definiendo Propiedades y el Constructor Primario
Las clases contienen propiedades (como nombre, apellido, edad), que vendrían siendo como los campos de la clase donde podemos guardar el estado. Al definirlas, es vital recordar la protección contra nulos de Kotlin. Debemos evitar que una propiedad sea nula; por ello, es recomendable asignar valores iniciales por defecto.
Kotlin define el constructor principal como parte de la clase en la cabecera/header de la misma, en donde los parámetros son opcionales. A diferencia de otros lenguajes, Kotlin permite definir el constructor primario directamente en la firma de la clase. Esto permite recibir parámetros y asignar propiedades de forma compacta:
class Persona(val nombre: String, var edad: Int)
Ya con esto, de manera automática Kotlin hace la equivalencia entre los parámetros en el constructor con las propiedades definidas en la clase.
Lea también: IVA 21% Excel
Tipos de Atributos: val y var
Al definir los atributos en Kotlin, utilizamos las palabras clave val y var para indicar su mutabilidad:
- Val (Constante): Se usa para valores que no cambian (como el nombre).
- Var (Variable): Se usa para valores que pueden mutar (como la edad).
Por optimización y buenas prácticas, intenta que todo sea un val a menos que sea estrictamente necesario cambiarlo. Supongamos que no queremos que esos objetos puedan ser cambiados, es información verídica que no requiere de cambio alguno; en estos casos, declarar propiedades con val asegura su inmutabilidad.
Para crear una instancia inicializando desde el constructor tenemos:
var persona = Persona("Andrés", 27)
Bloque init
También existe el bloque init, que actúa como el cuerpo del constructor primario para realizar inicializaciones o validaciones. Aquí podemos ver que al momento de crear nuestro objeto de tipo Person, su constructor se encuentra vacío p = Person(), pero lo habitual no será que nuestro constructor esté vacío, seguramente querremos pasarle un estado, identificar de esa persona cuáles son sus atributos. Luego necesitaríamos que nuestra clase guarde este estado en algún sitio.
Lea también: Guía IVA reducido
Herencia en Clases Kotlin
Por defecto, todas las clases en Kotlin son final (no se pueden heredar). Esto es un principio muy importante en Kotlin para ayudarnos a mantener organizados nuestras clases. Si quieres que una clase pueda heredarse, debes marcarla como open. Esto es una diferencia importante con Java, en donde podemos heredar de una clase sin necesidad de incorporar una palabra reservada que especifique que queremos que podamos heredar de la misma.
Al agregar open en nuestras clases estamos indicando que queremos diseñar dicha clase para que pueda ser heredada, lo que a la final permite facilitar la lectura del código de alguna aplicación realizada con Kotlin, independientemente de si es una aplicación para Android o para otra plataforma; también es importante señalar que no podemos heredar de múltiples clases o tener herencia múltiple.
Data Classes: Clases para el Manejo de Datos
Vamos a conocer otra estructura muy interesante: el Data Class. Como su nombre indica, es una clase pensada específicamente para trabajar con datos. Usualmente, las clases se emplean muchas veces solo como un depósito de datos, como si fueran un repositorio. Por ejemplo, cuando queremos crear un modelo de usuarios, un listado de publicaciones o cualquier estructura similar, simplemente almacenamos datos y no usamos lógica adicional.
El “problema” que tenemos con las clases normales es que una clase tradicional termina siendo bastante grande y compleja, con todas las características que ya vimos anteriormente (constructores, open, final, herencia, etc.), cosas que muchas veces no necesitamos cuando solo queremos manejar datos.
Por eso aparece el concepto de data class, que es una clase pensada específicamente para trabajar con datos, así de simple. De hecho, Kotlin es uno de los pocos lenguajes que incorpora este concepto de forma nativa.
Lea también: ¿Cómo localizar tus XML del SAT?
Ventajas de las Data Classes
Al usar data class:
- Te olvidas de cosas como open, final y demás configuraciones innecesarias.
- Son ideales cuando queremos manejar listados de datos (usuarios, productos, publicaciones, etc.).
- Son más eficientes que una clase normal al tener menos sobrecarga.
- Las comparaciones son muchísimo más sencillas.
Copiando Instancias y el Problema de la Referencia de Memoria
Para entender por qué las data class son útiles para copiar, debemos recordar cómo funcionan las clases normales. Si al asignar un objeto a otro, ambos apuntan a la misma memoria. Si tienes contact1 y creas contact2 igualándolo al primero, ambos apuntan al mismo sitio, la misma posición en memoria. En cambio, con tipos primitivos (como números), la asignación sí crea una copia del valor.
La forma más eficiente y común de copiar en Kotlin es usar una data class. Lo más potente de la función copy() es que puedes cambiar solo algunas propiedades mientras mantienes el resto igual.
Generando una Data Class
Una data class es una clase que contiene solamente atributos que queremos guardar, por ejemplo, datos de un superhéroe. Con esto conseguimos, por ejemplo, no tener varias variables «nombre1», «nombre2» para almacenar todos los superhéroes con los que vamos a trabajar.
Para crearla, en Android Studio, vamos al directorio donde tenemos el MainActivity y haciendo clic derecho seleccionamos New Kotlin File/class. Ahora nos saldrá un diálogo para añadir el nombre de la clase y justo debajo, en kind seleccionaremos class. Al darle a ok, nos habrá creado una clase.
A simple vista podemos ver que, a diferencia de una clase normal, esta no lleva llaves y la clase va precedida por «data». Esto le dice a Kotlin que esta clase va a tener por defecto una serie de funciones (aunque no vamos a tener que generarlas nosotros, lo hará el propio lenguaje por detrás) que podremos usar para rellenar dichos campos. La idea es tener un objeto Superhero por cada superhéroe que tengamos. Un objeto es simplemente la instancia de una clase.
Ejemplo de declaración:
data class Superhero(val name: String, val power: String)
Aunque la clase parezca muy sencilla, por detrás nos ha generado funciones para poder recuperar el valor de cada uno de los atributos, sustituirlos, compararlos, etc. Para generarlo usaremos el constructor por defecto que trae la clase. Por ejemplo:
var batman = Superhero("Batman", "Inteligencia")
batman.toString() //Devolverá todos los atributos con su valor
Tipos de Variables para Atributos
Una variable no es más que un espacio de memoria en el que podemos guardar información. Dependiendo del tipo de información, disponemos de diferentes variables, lo que nos permitirá evitar problemas como, por ejemplo, intentar sumar un número con una letra, ya que al ser tipos de variables diferentes no nos lo permitirá. Aunque Kotlin busca el tipo de valor y le asigna un tipo por detrás, es recomendable decirle nosotros el tipo explícitamente para evitar problemas:
var numeroFavorito: Int = 1
Esta vez le hemos puesto dos puntos y acto seguido hemos añadido el tipo de variable, en este caso Int. Ahora veremos con qué variables podemos trabajar al declarar atributos.
Variables Numéricas
Estas variables se usan para asignar números, calcular tamaños y realizar operaciones matemáticas, entre otras. Dentro de ellas se dividen en dos grupos, las variables enteras y reales.
- Integer (Enteras):
- Int: Es la más básica que usaremos, en la cual podremos insertar números naturales, pero hay una limitación. Con una variable de tipo Int no podemos pasar de -2,147,483,647 a 2,147,483,647. Este será el número máximo y mínimo que soportará.
var numeroFavorito: Int = -231
- Long: Básicamente es igual que Int, a diferencia de que soporta un rango mayor de números, de -9,223,372,036,854,775,807 a 9,223,372,036,854,775,807.
var numeroFavorito: Long = 47483647
- Int: Es la más básica que usaremos, en la cual podremos insertar números naturales, pero hay una limitación. Con una variable de tipo Int no podemos pasar de -2,147,483,647 a 2,147,483,647. Este será el número máximo y mínimo que soportará.
- Reales:
- Float: A diferencia de las anteriores, estas pueden almacenar decimales. Float soporta hasta 6 decimales, pero también puede trabajar con números enteros. Esta variable cambia un poco respecto a las demás, pues habrá que meter una «f» al final del valor.
var numeroFavorito: Float = 1.93f
- Double: Muy similar a Float pero soporta hasta 14 decimales. También ocupa más memoria, así que para un código óptimo deberemos pensar qué tipo será el que más se adapte a nuestro proyecto. No habrá que añadir ningún tipo de letra al final del valor.
var numeroFavorito: Double = 1.932214124
- Float: A diferencia de las anteriores, estas pueden almacenar decimales. Float soporta hasta 6 decimales, pero también puede trabajar con números enteros. Esta variable cambia un poco respecto a las demás, pues habrá que meter una «f» al final del valor.
Variables Alfanuméricas
Aunque los números sean muy útiles, hay veces que necesitaremos guardar cadenas de texto, o una mezcla de caracteres. Una String no es más que una cadena de Char.
var test: String = "Test. 12345!·$%&/"
Variables Booleanas
Los Booleanos son variables que solo pueden ser verdaderas o falsas (true o false). Su uso es muy amplio, cuando trabajemos con las condiciones veremos más a fondo este tema.
