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.
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 variablefield
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í.