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 title: String
init(title: String) {
self.title = title
}
}
class Movie: Media {
var director: String
init(title: String, director: String) {
self.director = director
super.init(title: title)}
}
}
class Song: Media {
var singer: String
init(title: String, singer: String) {
self.singer = singer
super.init(title: title)
}
}
Il y a donc trois classes. Les classes Song
et Movie
héritent de la classe Media
. Avec cette structure de données, je peux créer le tableau de médias suivants :
let library: [Media] = [
Movie(title: "Un balai dans le placard", director: "Rémi Movie"),
Song(title: "L'ombre de ta valise", singer: "Frank Patatra"),
Song(title: "Toi et moi dans le couloir", singer: "Johnny Les Vacances"),
Movie(title: "A portée de main", director: "Stanley Kubik"),
Movie(title: "Pourquoi pas ?", director: "Alfred Plicploc"),
Song(title: "De si bon matin", singer: "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érifiez 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 numberOfSong = 0
for media in library {
if media is Song { // Ici on contrôle que le media est bien une chanson
numberOfSong += 1
}
}
print("La librairie contient \(numberOfSong) chansons.")
Dans la boucle for, je ne sais pas si la variable media
est du type Song
ou du type Movie
. La déclaration if media is Song
, 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édez à 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 library {
if media is Song {
print(media.singer) // 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 Song
. 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 library {
if let song = media as? Song {
print(song.singer)
}
}
Ici nous venons de downcaster le type de la variable média.
Ah ? Et c’est grave docteur ? 🐰
Non, rassurez-vous, c’est assez courant. Caster est le principe qui consiste à forcer le typage d’une variable. Le as
nous permet de le faire en Swift. Il arrive que dans l’héritage nous ayons besoin de caster le type de notre variable. Le but ici est de pouvoir accéder à des variables plus spécifiques de notre classe fille.
Si j’avais voulu, à l'inverse, forcer le type de ma chanson pour pouvoir m’en servir comme d’un média, j’aurais upcasté le type de ma variable.
Voyons ce qu'il se passe en détail :
si la variable
media
est du typeSong
:une nouvelle variable est créée : la variable
song
de typeSong
, et qui contient les mêmes données que la variablemedia
;on effectue les instructions à l'intérieur des accolades. La variable
song
y est disponible.
si la variable
media
n'est pas du typeSong
: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 le as
si on est absolument certain du type de notre variable. Par exemple :
(library[1] as! Song).singer
Entre les parenthèses, on force l'évaluation de library[1]
à Song
, car on sait que c'est une chanson. Toute l'expression entre parenthèses est donc considérée comme une variable de type Song
dont on peut obtenir, du coup, la propriété singer
.
// Vérification que l'optionnel n'est pas nul
if optional != nil {
// Faire quelque chose
}
// Déclaration optionnelle
if let unwrapped = optional {
print(unwrapped)
}
// Déballage forcé
let unwrapped = optional!
Vous avez donc 3 méthodes pour vérifier les types : is
, as?
et as!
. Alors, comment décider quelle méthode utiliser ? Suivez le guide !
Utilisez 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 Int
, Float
, Double
, Bool
, String
. 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
OK, mais à quoi ça sert ?
Il y a deux usages principaux.
Créez des 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 mixArray: [Any] = [12, "Blob", true, Bus()]
Dans ce tableau, je peux utiliser des valeurs de différents types grâce au type Any
.
Vérifiez le type
Le deuxième cas courant d'utilisation, c'est quand on ne connaît 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 :
["error": "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]
.
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 courants de la vérification des types.
À vous de jouer !
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 classeHomeRoadSection
;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.
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
etAnyObject
.Pour vérifier un type, on a trois méthodes :
is
,as?
etas!
.Les types
Any
etAnyObject
permettent aux variables d'accepter des valeurs de n'importe quel type (uniquement les instances de classe pour le typeAnyObject
). 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.
Bravo, vous avez fini la partie sur l’héritage et vous voyez, tout s’est bien passé ! Un petit quiz pour vérifier si ces nouvelles notions sont bien comprises, et on se retrouve pour la dernière partie du cours, qui va vous permettre d'approfondir et de consolider vos connaissances sur la POO.