l'héritage

Le concept clé pour ne pas réinventer la roue et construire de nouvelles classes sur une base solide.

L'héritage est un concept fondamental de la programmation orientée objet (POO) qui permet de créer une nouvelle classe basée sur une classe existante. La nouvelle classe, appelée sous-classe ou classe dérivée, hérite des propriétés et méthodes de la classe parente ou classe de base, tout en ayant la possibilité de redéfinir ou d'ajouter ses propres propriétés et méthodes. L'héritage permet de réutiliser et d'étendre le code de manière efficace.

1. Concept de Base

Lorsqu'une classe hérite d'une autre, elle acquiert tous les attributs et méthodes de la classe parente. Cependant, elle peut également :

  • Ajouter ses propres propriétés et méthodes.
  • Redéfinir les méthodes existantes de la classe parente (surcharge ou override).
  • Accéder aux membres de la classe parente via le mot-clé super.

2. Définir une Sous-classe

Pour créer une sous-classe en Swift, vous utilisez le mot-clé class suivi du nom de la sous-classe, puis du nom de la classe parente après un signe deux-points :.

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
    func makeSound() {
        print("\(name) fait un bruit.")
    }
}

class Dog: Animal {
    var breed: String
    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)  // Appel de l'initialiseur de la classe parente
    }
    // Surcharge de la méthode `makeSound`
    override func makeSound() {
        print("\(name) aboie.")
    }
    func fetch() {
        print("\(name) va chercher.")
    }
}

let myDog = Dog(name: "Rex", breed: "Golden Retriever")
myDog.makeSound()  // Affiche : "Rex aboie."
myDog.fetch()  // Affiche : "Rex va chercher."

3. Accès aux Membres de la Classe Parente

Les sous-classes peuvent accéder aux propriétés et méthodes de la classe parente en utilisant le mot-clé super. Cela est souvent utilisé pour appeler l'initialiseur de la classe parente ou pour accéder aux méthodes et propriétés surchargées.

class Cat: Animal {
    override func makeSound() {
        super.makeSound()  // Appel de la méthode de la classe parente
        print("\(name) miaule.")
    }
}

let myCat = Cat(name: "Whiskers")
myCat.makeSound()
// Affiche :
// "Whiskers fait un bruit."
// "Whiskers miaule."

4. Surcharge de Méthode (Method Overriding)

Lorsqu'une sous-classe redéfinit une méthode de la classe parente, on parle de surcharge de méthode (method overriding). Pour surcharger une méthode, vous devez utiliser le mot-clé override.

class Vehicle {
    func start() {
        print("Le véhicule démarre.")
    }
}

class Car: Vehicle {
    override func start() {
        print("La voiture démarre.")
    }
}

let vehicle = Vehicle()
vehicle.start()  // Affiche : "Le véhicule démarre."
let car = Car()
car.start()  // Affiche : "La voiture démarre."

5. Héritage et Initialisation

Lorsqu'une sous-classe a son propre initialiseur, elle doit initialiser ses propres propriétés avant d'appeler l'initialiseur de la classe parente. Cela garantit que toutes les propriétés, héritées ou nouvelles, sont correctement initialisées.

class Bicycle: Vehicle {
    var hasBasket: Bool
    init(hasBasket: Bool) {
        self.hasBasket = hasBasket
        super.init()  // Appel de l'initialiseur de la classe parente
    }
    override func start() {
        print("Le vélo démarre.")
    }
}

let bike = Bicycle(hasBasket: true)
bike.start()  // Affiche : "Le vélo démarre."

6. Propriétés et Méthodes Finales

Si vous ne voulez pas qu'une sous-classe puisse surcharger une méthode ou hériter d'une propriété, vous pouvez marquer cette méthode ou propriété avec le mot-clé final. Une classe entière peut également être marquée final, ce qui empêche toute classe de la sous-classer.

class Animal {
    final func sleep() {
        print("L'animal dort.")
    }
}

class Dog: Animal {
    // Essayer de surcharger `sleep` provoquerait une erreur de compilation
    // override func sleep() { } // Erreur: Impossible de surcharger une méthode finale
}

7. Héritage Multiple et Classes Abstraites

Contrairement à certains langages comme C++, Swift ne supporte pas l'héritage multiple (une sous-classe ne peut pas hériter de plusieurs classes à la fois). Cependant, Swift permet d'utiliser des protocoles pour combiner différents comportements.

De plus, Swift ne supporte pas directement les classes abstraites (une classe que l'on ne peut pas instancier directement), mais vous pouvez utiliser une combinaison de classes de base et de protocoles pour simuler ce comportement.

protocol CanFly {
    func fly()
}

class Bird: CanFly {
    func fly() {
        print("L'oiseau vole.")
    }
}

class Penguin: Bird {
    // Les pingouins ne volent pas, donc nous n'implémentons pas `fly`.
}

let penguin = Penguin()
// penguin.fly()  // Erreur: "Penguin" does not override 'fly()'

8. Casting avec l'Héritage

Lorsqu'on travaille avec des objets hérités, il est parfois nécessaire de les convertir d'un type à un autre (casting). En Swift, cela se fait à l'aide des opérateurs as, as?, et as!.

let animal: Animal = Dog(name: "Buddy", breed: "Labrador")
if let dog = animal as? Dog {
    print("\(dog.name) est un \(dog.breed).")  // Affiche : "Buddy est un Labrador."
}