Getters y Setters

Explicación de los métodos get y set.

Hasta ahora tenemos nuestra clase Slime de la siguiente manera:

val BAD_WORDS: Array<String> = arrayOf("Feo", "Tonto", "CuatroOjos")

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    var name: String
    var hp: Int
    var ap: Int
    var alive: Boolean = true

    init {
        if (name in BAD_WORDS) this.name = "BuenIntento" else this.name = name
        if (hp <= 100) this.hp = hp else this.hp = Random.nextInt(0, 100)
        if (ap <= 5) this.ap = ap else this.ap = Random.nextInt(0, 5)
    }  
    fun die() {
    this.alive = false
    }
}

Hemos diseñado un constructor que evita que el usuario pueda introducir valores superiores a 100 en hp y a 5 en ap.

Sin embargo, el usuario aun puede acceder a dichos valores como miembros que son de la clase y modificarlos a su antojo, evitando dicha comprobación del constructor:

    val azulito: Slime = Slime("CuatroOjos", 1000000, 500)
    
    println("name: " + azulito.name)    // name: azulito
    println("hp: " + azulito.hp)        // hp: 53 (aleatorio 0  a 100)
    println("ap: " + azulito.ap)        // ap: 2 (aleatorio 0 a 5)
    println("alive: " + azulito.alive)  // alive: true
    azulito.hp = 1000000
    azulito.ap = 500
    println("hp: " + azulito.hp)        // hp: 1000000
    println("ap: " + azulito.ap)        // ap: 500

Para evitar eso debemos utilizar los modificadores de visibilidad que permiten restringir el acceso a diferentes partes del código (variables, clases, constructores...):

  • private: Marca una declaración como visible en la clase o archivo actual.

  • protected: Marca una declaración como visible en la clase y subclases de la misma.

  • internal: Marca una declaración como visible en el módulo actual.

  • public: Marca una declaración como visible en todas partes.

De esta manera estaríamos aplicando el principio de ocultación:

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    private val name: String
    private var hp: Int
    private var ap: Int
    private var alive: Boolean = true
    /*...*/
// Las lineas de abajo, ahora darán error.
println("name: " + azulito.name)
println("hp: " + azulito.hp)
println("ap: " + azulito.ap)
println("alive: " + azulito.alive)
azulito.hp = 1000000
azulito.ap = 500

Sin embargo, ahora no podemos acceder a la información almacenada en las variables ni modificarla. Para eso se utilizan las funciones get y las funciones set.

APLICACIÓN DE ENCAPSULAMIENTO

Funciones get()

Las funciones get() nos permiten acceder a la información almacenada dentro de las propiedades de un objeto:

Cuando se crea un atributo val, el compilador crea una función get() por defecto que es la que en el ejemplo inmediatamente inferior estamos personalizando.

No se crea función set() ya que una val no puede modificar su valor.

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    var name: String    // esta no es private para el ejemplo
        get() = "Este Slime se llama: $field."
    private var hp: Int
        get() = "Este Slime tiene $field Puntos de Salud."
    private var ap: Int
        get() = "Este Slime tiene $field Puntos de Ataque."
    private var alive: Boolean = true
        get() = field
println(azulito.name) // Este Slime se llama: Azulito.

Sin embargo, al haber hecho nuestras variables privadas, no va a servir esta forma de ejecutar los getters ya que no tenemos capacidad de visualizarlos.

Tendremos que utilizar la siguiente opción. Crear funciones miembro de la clase que hagan el trabajo de las funciones get():

val BAD_WORDS: Array<String> = arrayOf("Feo", "Tonto", "CuatroOjos")

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    private val name: String
    private var hp: Int
    private var ap: Int
    private var alive: Boolean = true

    init {
        if (name in BAD_WORDS) this.name = "BuenIntento" else this.name = name
        if (hp <= 100) this.hp = hp else this.hp = Random.nextInt(0, 100)
        if (ap <= 5) this.ap = ap else this.ap = Random.nextInt(0, 5)
    }
    
    fun getName(): String = this.name
    fun getHp(): Int = this.hp
    fun getAp(): Int = this.ap
    fun getAlive(): Boolean = this.alive
        
    fun die() {
    this.alive = false
    }
}
println("name: " + azulito.getName())
println("hp: " + azulito.getHp())
println("ap: " + azulito.getAp())
println("alive: " + azulito.getAlive())

Funciones set()

Se utilizan para modificar el contenido de un atributo.

Cuando se crea un atributo var, el compilador crea una función get() por defecto, así como una función set() que es la que en el ejemplo inmediatamente inferior estamos personalizando.

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    var name: String    // esta no es private para el ejemplo
        get() = "Este Slime se llama: $field."
    private var hp: Int
        get() = "Este Slime tiene $field Puntos de Salud."
        set(value) {
            if (value > 100) field = Random.NextInt(0, 100) else field = value
        }
    private var ap: Int
        get() = "Este Slime tiene $field Puntos de Ataque."
        set(value) {
            if (value > 5) field = Random.NextInt(0, 5) else field = value
        }
    private var alive: Boolean = true
        get() = field

Sin embargo, como ha pasado anteriormente, no podemos hacer esto pues hemos hecho privados nuestros atributos.

Para solventarlo vamos a crear unas funciones que actuen como los setter que hemos definido arriba:

val BAD_WORDS: Array<String> = arrayOf("Feo", "Tonto", "CuatroOjos")

class Slime (name: String = "slimePorDefecto", hp: Int = 100, ap: Int = 5){
    private val name: String
    private var hp: Int
    private var ap: Int
    private var alive: Boolean = true

    init {
        if (name in BAD_WORDS) this.name = "BuenIntento" else this.name = name
        if (hp <= 100) this.hp = hp else this.hp = Random.nextInt(0, 100)
        if (ap <= 5) this.ap = ap else this.ap = Random.nextInt(0, 5)
    }
    fun getName(): String = this.name
    fun getHp(): Int = this.hp
    fun getAp(): Int = this.ap
    fun getAlive(): Boolean = this.alive

    fun setHp(value: Int) {
        if (value > 100) this.hp = Random.nextInt(0, 100) else this.hp = value
    }
    fun setAp(value: Int) {
        if (value > 5) this.ap = Random.nextInt(0, 5) else this.ap = value
    }

    fun die() {
        this.alive = false
    }
}

Last updated