Constructores Designados y de Conveniencia en Herencia de Clases

Clase 20 de 27Curso de Programación Orientada a Objetos en Swift

Resumen

Comprender los inicializadores en Swift te permite crear jerarquías de clases robustas y predecibles. Aquí verás cómo funcionan los inicializadores designados y los inicializadores por conveniencia, cómo interactúan con la herencia, y por qué llamadas como super.init y override son claves para mantener un flujo de construcción correcto.

¿Qué son los inicializadores designados y de conveniencia en Swift?

Los inicializadores organizan el flujo de creación de objetos en clases y subclases. En herencia, ese flujo debe ser claro y seguro para que cada clase configure sus propias variables y las herede correctamente.

¿Qué reglas rigen el llamado de init en herencia?

  • Un inicializador designado solo puede llamar a un designado de la superclase.
  • Un inicializador de conveniencia solo puede llamar a otro init de la misma clase (de conveniencia o designado).
  • El último init llamado siempre debe ser designado. Si no, la inicialización fallará.

¿Cómo aplicarlas en clases y subclases con Vehicle y Bicycle?

Partimos de una clase base con un valor por defecto y una propiedad computada. Así, no es obligatorio escribir un init explícito para la clase padre.

¿Cómo se inicializa Vehicle y qué imprime description?

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) ruedas"
    }
}

let vehicle = Vehicle()
print(vehicle.description) // "0 ruedas"
  • numberOfWheels inicia en 0.
  • description devuelve un texto basado en el número de ruedas.

¿Por qué override init debe llamar a super.init en Bicycle?

Al sobreescribir con override, el init del hijo sustituye al del padre. Para mantener el orden correcto, el designado del hijo debe delegar en el designado del padre con super.init.

class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}

let bicycle = Bicycle()
print(bicycle.description) // "2 ruedas"
  • override init indica que el hijo redefine el proceso.
  • super.init() asegura que el padre prepare su parte antes de ajustar las del hijo.

¿Cómo fluye un inicializador de conveniencia con HoverBoard y color?

Cuando el hijo introduce propiedades propias, como un color, se puede usar un flujo de conveniencia: primero se configuran las variables del hijo y, de forma implícita, se llama al init del padre para completar la estructura base. Luego se puede personalizar la salida combinando la descripción del padre con los datos del hijo.

¿Cómo combina HoverBoard sus propiedades con la descripción del padre?

class HoverBoard: Vehicle {
    var color: String

    init(color: String) {
        self.color = color
        // Flujo descrito: se inicializa la superclase.
        super.init()
    }

    override var description: String {
        return "\(super.description) en color \(self.color)"
    }
}

let hoverboard = HoverBoard(color: "silver")
print(hoverboard.description) // "0 ruedas en color silver"
  • color es propio de HoverBoard y se configura con self.color = color.
  • super.description reutiliza la descripción del padre y la enriquece con el color.
  • El resultado muestra cómo el init del padre fija las ruedas en 0 y el hijo añade su detalle.

¿Tienes ejemplos propios con subclases y override que quieras validar? Cuéntalos y probamos juntos el flujo de inicialización con super.init y descripciones personalizadas.