• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/1/19

Contrôlez vos types

Log in or subscribe for free to enjoy all this course has to offer!

L'héritage, c'est bien, mais ça a un petit défaut. On peut se mélanger un peu les types parfois. Laissez-moi vous donner un exemple. Prenons les classes suivantes :

class Media {
    var titre: String

    init(titre: String) {
        self.titre = titre
    }
}

class Film: Media {
    var réalisateur: String

    init(titre: String, réalisateur: String) {
        self.réalisateur = réalisateur
        super.init(titre: titre)
    }
}

class Chanson: Media {
    var chanteur: String

    init(titre: String, chanteur: String) {
        self.chanteur = chanteur
        super.init(titre: titre)
    }
}

Il y a donc trois classes. Les classes Chanson et Film héritent de la classe Media. Avec cette structure de données, je peux créer le tableau de médias suivants :

let librairie = [
    Film(titre: "Un balai dans le placard", réalisateur: "Rémi Movie"),
    Chanson(titre: "L'ombre de ta valise", chanteur: "Frank Patatra"),
    Chanson(titre: "Toi et moi dans le couloir", chanteur: "Johnny Les Vacances"),
    Film(titre: "A portée de main", réalisateur: "Stanley Kubik"),
    Film(titre: "Pourquoi pas ?", réalisateur: "Alfred Plicploc"),
    Chanson(titre: "De si bon matin", chanteur: "Alain Chausson")
]

Ce tableau est parfaitement valable. En effet, il est du type [Media] et il ne contient que des médias. Mais à cause de l'héritage, il y a maintenant deux types de médias ! Comment faire pour les différencier ? Par exemple, comment faire si je veux compter le nombre de chansons dans le tableau ?

Vérifier un type

Pour cela, nous allons découvrir ensemble le mot clé is. Le mot clé is permet d'inspecter le type d'un objet. Avec cet outil, essayons de compter le nombre de chansons dans la librairie :

var nombreDeChansons = 0

for media in librairie {
    if media is Chanson { // Ici on contrôle que le media est bien une chanson
        nombreDeChansons += 1
    }
}

print("La librairie contient \(nombreDeChansons) chansons.")

Dans la boucle for, je ne sais pas si la variable media est du type Chanson ou du type Film. La déclaration if media is Chanson qui se traduit littéralement "si le média est une chanson" nous permet de vérifier le type du média. Si c'est le cas, on incrémente notre compteur.

Le mot-clé is s'utilise donc avec la syntaxe variable is Type et renvoie un booléen.

Accéder à un sous-type

En reprenant, notre exemple de librairie, j'aimerais maintenant afficher tous les chanteurs de toutes les chansons de la librairie. Essayons la même stratégie :

for media in librairie {
    if media is Chanson {
        print(media.chanteur) // ERREUR
    }
}

On a une erreur, car même si on a contrôlé le type de media, Swift considère toujours que media est juste du type Media et pas du type Chanson. Et donc la propriété chanteur n'existe pas. C'est toute la rigueur de Swift, une même variable ne changera JAMAIS de type même si on prend beaucoup de précautions.

Il va donc falloir créer une nouvelle variable et on va faire cela en utilisant l'opérateur as comme ceci :

for media in librairie {
    if let chanson = media as? Chanson {
        print(chanson.chanteur)
    }
}

Voyons ce qu'il se passe en détail :

  • Si la variable media est du type Chanson :

    • Une nouvelle variable est créée : la variable chanson de type Chanson et qui contient les mêmes données que la variable media.

    • On effectue les instructions à l'intérieur des accolades. La variable chanson y est disponible.

  • Si la variable media n'est pas du type Chanson :

    • Aucune variable n'est créée.

    • On ignore les instructions entre accolades.

Il nous reste une question à voir, celle du point d'interrogation. Le point d'interrogation après le as signifie que cette opération peut échouer. En effet, la variable media peut ne pas être du type chanson.

On peut également utiliser le point d'exclamation après leassi on est absolument certain du type de notre variable. Par exemple :

(librairie[1] as! Chanson).chanteur

Entre les parenthèses, on force l'évaluation de librairie[1] à Chanson, car on sait que c'est une chanson. Toute l'expression entre parenthèses est donc considérée comme une variable de type Chanson dont on peut obtenir du coup la propriété chanteur.

Utilisation

Vous avez donc 3 méthodes pour vérifier les types : isas? et as!. Alors, comment décider quelle méthode utiliser ? Suivez le guide !

Les types Any et AnyObject

Any

Il y a un type un petit peu particulier que nous allons découvrir maintenant : le type Any. Une variable de type Any peut accepter une valeur de n'importe quel type :

let a: Any = 12 // Ça marche
let b: Any = "Blob" // Ça marche aussi
let c: Any = true  // Ça marche encore
let d: Any = Bus() // Ça marche toujours !

Encore plus fort, une variable de type Any peut changer de valeur avec des valeurs de types différents.

var a: Any = 12
a = "Blob"
a = true
a = Bus()

AnyObject

Le type Any a un petit frère que vous risquez de rencontrer : le type AnyObject. Quelle est la différence ? Le type AnyObject fonctionne uniquement avec les instances d'une classe. Cela exclut notamment tous les types basiques qui sont des structures en Swift (et non des classes) comme IntFloatDoubleBoolString. En résumé :

var a: AnyObject = 12 // Renvoie une erreur
var a: Any = 12 // OK

var b: AnyObject = Bus() // OK
var b: Any = Bus() // OK

Usages

OK, mais à quoi ça sert ?

Il y a deux usages principaux.

Collections de types mixés

Le premier usage courant, c'est de pouvoir créer des collections de types mixés par exemple comme ceci :

var tableauMixé: [Any] = [12, "Blob", true, Bus()]

Dans ce tableau, je peux utiliser des valeurs de différents types grâce au type Any.

Objet de type inconnu

Le deuxième cas courant d'utilisation, c'est quand on ne sait pas le type d'un objet que l'on va utiliser.

Prenons l'exemple d'une requête réseau. Je souhaite récupérer l'âge d'un utilisateur. Le serveur peut me répondre de deux façons différentes :

  • Tout se passe bien, je reçois : ["age": 22]. La réponse est donc du type : [String: Int].

  • Il y a une erreur, je reçois : ["erreur": "Le serveur a planté !"]. La réponse est cette fois du type [String: String].
    Donc dans cet exemple, je peux recevoir une variable de deux types différents. Pour pouvoir gérer ces deux types, je vais déclarer que ma variable est du type : [String: Any].

Vérification du type

La raison pour laquelle je vous parle des types Any et AnyObject maintenant, c'est qu'avec des variables de ce type, on va souvent avoir besoin de vérifier le type qu'elles ont réellement avant de pouvoir les utiliser. Les types Any et AnyObject sont donc un des cas d'usage très courant de la vérification des types.

Exercice

Notre méthode drive de SchoolBus permet au bus de s'arrêter à chaque maison. Mais il ne récupère pas encore d'enfants. Dans cet exercice, vous allez rectifier ça :

  • À chaque maison, le bus récupère les enfants. Pour cela vous utiliserez la propriété occupiedSeats de la classe Bus et la propriété children de la class HomeRoadSection.

  • Le bus ne peut pas récupérer plus d'enfants que sa capacité de places assises (définie par la propriété seats).

  • Arrivé à l'école, le bus dépose tous les enfants

// Ne regardez pas la correction


























override func drive(road: Road) {
    for section in road.sections {
        switch section.type {
        case .plain:
            moveForward()
        case .home:
            if shouldPickChildren() {
                pickChildren(from: section)
                stop()
            }
            moveForward()
        case .school:
            dropChildren()
            stop()
        }
    }
}

func shouldPickChildren() -> Bool {
    return occupiedSeats < seats
}

func pickChildren(from roadSection: RoadSection) {
    if let section = roadSection as? HomeRoadSection {
        occupiedSeats += section.children
    }
}

func dropChildren() {
    occupiedSeats = 0
}

En résumé

  • Les deux cas d'usage les plus courants de la vérification des types sont : l'héritage et les types Any et AnyObject.

  • Pour vérifier un type, on a trois méthodes : isas? et as!.

  • Pour choisir la méthode à utiliser, vous pouvez vous référer à ce schéma :

  • Les types Any et AnyObject permettent aux variables d'accepter des valeurs de n'importe quel type (uniquement les instances de classe pour le type AnyObject). On les utilise lorsqu'on veut faire des collections de types mixés ou qu'on ne connaît pas le type d'une variable que l'on va utiliser.

Example of certificate of achievement
Example of certificate of achievement