Kotlin – POO. Clases, constructores, getters y setters

Resumen: en este post voy a explicar cómo usar la programación orientada a objetos (POO u OOP) en Kotlin.

También voy a dar un repaso y ejemplos de código para declarar clases, crear objetos, definir constructores y los getters y setters.

Programación orientada a objetos en Kotlin

Tema de la imagen: SynthWave ’84.

De igual forma explicaré los niveles de acceso, que son private, protected, internal y public.

Declaración de una clase

Para declarar una clase en Kotlin se utiliza la palabra class, seguida del nombre de la clase. Por ejemplo:

class Mascota{}

Veamos un ejemplo sencillo de una clase en Kotlin:

/**
 * Clase en Kotlin
 *
 * @author parzibyte
 * @link https://parzibyte.me/blog
 *
 * */
class Mascota {
    var nombre: String

    init {
        println("Init del objeto")
        nombre = ""
    }

    constructor(nombre: String) {
        this.nombre = nombre
        // Aquí el constructor
        println("Se construye el objeto")
    }


    fun dormir() {
        println("ZZZZ $nombre duerme")
    }

}

Las propiedades de la clase se declaran con var o con val, y siguen las mismas reglas que cualquier variable.

Podemos tener funciones dentro de la clase, y se puede acceder a las propiedades de la misma.

El init es para establecer el valor inicial de las variables. Es muy útil cuando declaramos con val y las mismas se deben inicializar obligatoriamente.

Dentro de una clase se puede tener varios constructores con distinta firma, es decir, que reciban distintos argumentos o una cantidad diferente. Lo vamos a ver más abajo.

Instanciar una clase

Para crear un objeto o una instancia de una clase, se hace así:

val instancia = Clase()

(también podrías usar var, aunque no es recomendable)

No necesitamos ninguna palabra reservada como new o algo así. Después de tener un objeto de la clase podemos llamar a los métodos de la misma.

Veamos el ejemplo de creación de un objeto en Kotlin:

fun main(argumentos: Array<String>) {
    val mascota = Mascota("Maggie")
    mascota.dormir()
    println("El nombre de la mascota: ${mascota.nombre}")
}

En la línea 2 hago la instancia de la clase, pasando el nombre de la mascota (pues es un argumento necesario)

En la línea 3 invoco a un método de la clase que internamente imprime un mensaje usando las backticks.

Como la propiedad nombre es pública, puedo acceder a ella desde la instancia.

Puedes probar el código aquí.

Modificadores de acceso

Kotlin tiene 4 modificadores de acceso:

public: por defecto las funciones y variables son públicas. Es decir, especificarlo con public o no especificar su acceso es lo mismo.

private: solo se puede acceder desde la clase. No se puede acceder a instancia.propiedad desde fuera de la clase.

internal: cualquiera (dentro del mismo módulo) que pueda acceder a la clase puede acceder a la propiedad.

protected: es como private, pero las subclases de de esta clase pueden acceder a la propiedad.

Constructores

Pueden existir varios constructores dentro de una clase de Kotlin. Todos ellos son definidos con la palabra reservada constructor.

En Kotlin existe el constructor primario, el cual puede ser definido dentro de la definición de la clase.

Por ejemplo, la clase que estaba así:

/**
 * Clase en Kotlin
 *
 * @author parzibyte
 * @link https://parzibyte.me/blog
 *
 * */
class Mascota {
    var nombre: String

    init {
        println("Init del objeto")
        nombre = ""
    }

    constructor(nombre: String) {
        this.nombre = nombre
        // Aquí el constructor
        println("Se construye el objeto")
    }


    fun dormir() {
        println("ZZZZ $nombre duerme")
    }

}

Puede definirse así:

/**
 * Clase en Kotlin
 *
 * @author parzibyte
 * @link https://parzibyte.me/blog
 *
 * */
class Mascota(var nombre: String) {

    init {
        println("Init del objeto")
        nombre = ""
    }


    fun dormir() {
        println("ZZZZ $nombre duerme")
    }

}

Mira la línea 8, dentro de la misma se declara la propiedad y el constructor.

Recuerda: el init no es obligatorio, pero en él puedes iniciar los elementos de la clase o simplemente hacer lo que se hace al construir tu objeto.

Varios constructores

Además del constructor primario, pueden existir más constructores. Los mismos deben llamar al constructor primario.

Veamos este ejemplo en donde se recibe, además del nombre, la edad de la mascota.

/**
 * Clase en Kotlin
 *
 * @author parzibyte
 * @link https://parzibyte.me/blog
 *
 * */
class Mascota(var nombre: String, var edad: Int) {

    constructor(nombre: String) : this(nombre, 0) {
        // Opcionalmente aquí el cuerpo u otras acciones
    }


    fun dormir() {
        println("ZZZZ $nombre duerme")
    }

    fun saludar() {
        println("¡Hola! me llamo $nombre y tengo $edad años. Woof")
    }

}

El constructor primario necesita el nombre y la edad (línea 8)

Pero en la línea 10 definimos otro constructor, un constructor secundario en Kotlin. El mismo solo recibe el nombre, y llama a this (es decir, al constructor primario) estableciendo la edad en 0, que es el valor por defecto.

Opcionalmente, el segundo constructor puede tener un cuerpo en donde hacemos más acciones además de llamar al constructor primario.

La forma de invocación podría ser la siguiente:

fun main(argumentos: Array<String>) {
    val mascota = Mascota("Maggie")
    mascota.dormir()
    mascota.saludar()
    val otraMascota = Mascota("Guayaba", 2)
    otraMascota.saludar()
    /*
    * Salida:
    ZZZZ Maggie duerme
    ¡Hola! me llamo Maggie y tengo 0 años. Woof
    ¡Hola! me llamo Guayaba y tengo 2 años. Woof
    */
}

Tenemos dos instancias. Una de ellas es creada sin especificar la edad (por lo que ahora es 0 según nuestro segundo constructor)

La otra, se define con la edad.

Puedes probar el código aquí.

Getters y setters

Los gettes y setters en Kotlin son implícitos y heredan el mismo acceso que las propiedades.

Veamos cómo definir los getters y setters, y cómo declararlos de forma explícita.

Nota: ya lo dije pero lo digo de nuevo. Los getters y setters en Kotlin heredan el mismo acceso que las propiedades. Así que si defines una propiedad como privada, el getter y setter también serán privados y esto no puede ser cambiado.

Si quieres proteger tus propiedades pero poner su modificador público, crea una función que no lleve el nombre de getTuPropiedad y setTuPropiedad, es decir, coloca un nombre distinto.

Get y set en Kotlin: implícito

Por defecto se definen estas funciones sin nosotros saberlo.

Es decir, si definimos la siguiente propiedad:

var nombre: String

Los getters y setters se crean y se usan así:

mascota.nombre = "Otro nombre"

var elNombre = mascota.nombre

No hay necesidad de llamar a funciones como getNombre y setNombre, simplemente hay que acceder a la propiedad o establecerla.

Nota: internamente Kotlin definirá y llamará a getNombre y setNombre.

Get y set en Kotlin explícito

Ahora veamos de nuevo nuestra clase:

/**
 * Clase en Kotlin
 *
 * @author parzibyte
 * @link https://parzibyte.me/blog
 *
 * */
class Mascota {
    var nombre: String
        get() {
            println("Llamada al getter de nombre")
            return field
        }
        set(nuevoNombre) {
            println("Llamada al setter de nombre")
            field = nuevoNombre
        }

    // Para edad, dejamos el setter y getter implícito
    var edad: Int


    constructor(nombre: String, edad: Int) {
        this.nombre = nombre
        this.edad = edad
    }

    fun saludar() {
        println("¡Hola! me llamo $nombre y tengo $edad años. Woof")
    }

}

Abajo de la declaración de la propiedad he establecido la función get y set. Lo único que hago en el cuerpo es imprimir que estamos usando las funciones.

Como puedes ver, dentro de los getters y setters se utiliza field. Esta variable field es especial y se refiere / apunta al campo.

En la línea 12 no podemos regresar this.nombre ya que estaríamos llamando al get, es decir, sería una recursión. Por eso es que mejor se devuelve field, que apunta a la variable en sí.

Para el setter (línea 14) es lo mismo, se recibe un valor y se le asigna eso a field. No podemos hacer un this.nombre = nuevoNombre porque se llamaría al setter, creando una recursividad.

Podemos ejemplificar esto con el siguiente código:

fun main(argumentos: Array<String>) {
    val mascota = Mascota("Guayaba", 2)
    val elNombre = mascota.nombre // se llama a getNombre internamente
    println("El nombre de la mascota es $elNombre")
    val laEdad = mascota.edad // Llamar a getEdad. Como no definimos el getter, no se imprime nada
    println("La edad es: $laEdad")

    // Cambiamos, llamamos a setNombre
    mascota.nombre = "Panqué"
    // Y ahora de nuevo a getNombre
    println("El nuevo nombre es: ${mascota.nombre}")
    
    /*
    * Salida:
    * Primero el constructor:
    Llamada al setter de nombre
    * Luego obtenemos el nombre y la edad
    Llamada al getter de nombre
    El nombre de la mascota es Guayaba
    La edad es: 2
    * Más tarde, llamada al setter e inmediatamente al getter
    Llamada al setter de nombre
    Llamada al getter de nombre
    El nuevo nombre es: Panqué
    * */
}

Recuerda que el getter y setter de la edad no han sido modificados, en cambio los del nombre sí.

En el constructor (línea 23 de la clase) se llama a this.edad = edad así que invocamos al setter, y como lo hemos modificado, se imprime dicha modificación.

Así es como podemos poner un cuerpo al setter y getter de las propiedades de una clase. Por defecto Kotlin crea uno por nosotros pero podemos “sobrescribirlos”

Prueba el código aquí.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *