Saltar a contenido

4.C Constructores y enumerados

Más de una forma de crear un objeto

A veces un objeto se puede crear de varias maneras: con unos valores por defecto, con datos propios o a partir de una «receta» ya preparada. Cada lenguaje lo resuelve a su manera.

Kotlin usa parámetros con valor por defecto (nombre: String = "Margarita"), que sustituyen casi siempre a la sobrecarga, y se pueden pasar por nombre (Pizza(Tamano.MEDIANA, extras = 2)). Para constructores alternativos con otro significado se usan funciones de fábrica en el companion object (Pizza.cuatroQuesos(...)) o constructores secundarios (constructor(...)).

enum class Tamano(val etiqueta: String, val precioBase: Int) {
    PEQUENA("Pequeña", 6),
    MEDIANA("Mediana", 8),
    GRANDE("Grande", 10),
}

class Pizza(val tamano: Tamano, val nombre: String = "Margarita", extras: Int = 0) {
    var extras = extras
        private set

    fun anadirExtra(cantidad: Int = 1) {
        extras += cantidad
    }

    fun precio() = tamano.precioBase + extras

    override fun toString() = "Pizza($nombre, ${tamano.etiqueta}, $extras extras, ${precio()} €)"

    companion object {
        fun cuatroQuesos(tamano: Tamano) = Pizza(tamano, "Cuatro quesos", 3)
    }
}

fun main() {
    val a = Pizza(Tamano.MEDIANA)
    val b = Pizza.cuatroQuesos(Tamano.GRANDE)
    val c = Pizza(Tamano.PEQUENA, "Barbacoa")
    println(a)
    println(b)
    println(c)
    a.anadirExtra()
    a.anadirExtra(2)
    println(a)
    println("total del pedido: ${a.precio() + b.precio() + c.precio()} €")
    println("tamaños: ${Tamano.entries.joinToString(", ") { it.etiqueta }}")
}

Salida:

Pizza(Margarita, Mediana, 0 extras, 8 €)
Pizza(Cuatro quesos, Grande, 3 extras, 13 €)
Pizza(Barbacoa, Pequeña, 0 extras, 6 €)
Pizza(Margarita, Mediana, 3 extras, 11 €)
total del pedido: 30 €
tamaños: Pequeña, Mediana, Grande

En este ejemplo hay tres formas de obtener una pizza: solo con el tamaño (el nombre por defecto es «Margarita»), con nombre propio (Barbacoa) y la receta ya preparada con un constructor alternativo (cuatroQuesos, que además trae tres extras). Después, anadirExtra se llama con y sin argumento.

Para un método que se puede llamar con o sin argumento, como anadirExtra() y anadirExtra(2), se usa un solo método con un parámetro con valor por defecto (cantidad: Int = 1).

Un solo sitio para las reglas

Procura que todos los constructores acaben pasando por el mismo código (un constructor principal al que los demás llaman): así cualquier regla que añadas más adelante (por ejemplo, un máximo de extras) se escribe una sola vez.

Enumerados

Un enumerado es un tipo con un conjunto fijo y cerrado de valores: los días de la semana, los estados de un pedido, los tamaños de una pizza. Es mejor que usar números o textos sueltos porque el compilador impide valores inventados (Tamano.gigante no existe) y el código se lee solo.

Un enum class en Kotlin puede tener propiedades en el constructor y métodos. Tamano.entries devuelve todos los valores en orden. Por convención las constantes van en MAYÚSCULAS.

En el ejemplo, cada tamaño lleva datos asociados (la etiqueta que se muestra y el precio base), de modo que el resto del programa no necesita un if por cada tamaño: pregunta el dato al propio valor.

Errores frecuentes

Error Cómo evitarlo
Repetir las comprobaciones en cada constructor Un constructor principal y los demás que lo llaman
Dos constructores casi iguales con distinto orden de parámetros Usa valores por defecto o un método de fábrica con nombre claro
Usar números o textos para representar categorías (tipo = 1) Un enumerado
Cadenas de if/else según el valor de un enumerado Pon el dato o el comportamiento dentro del enumerado

Para practicar

Los constructores, los enumerados y la sobrecarga se practican en U4.6 · Prueba. Compara cómo se escribe en otro lenguaje con Pasar de uno a otro.