Dans le langage Swift, le mécanisme de conversion de type sert à vérifier et à transformer une valeur d'un type à un autre pendant l'exécution. Ses origines résident dans la typage statique et l'héritage : en Swift, on peut travailler simultanément avec des types value (struct/enum) et des types reference (class/protocol). Le problème survient lorsque l'objet ou la valeur est assigné à une variable de type Any ou à un protocole — et il faut déterminer s'il est possible de le convertir à un autre type (souvent plus spécifique) sans risquer de faire planter l'application.
La conversion de type sécurisée permet d'utiliser les avantages de la sécurité de type, du polymorphisme dynamique et garantit la protection contre les erreurs lors de la manipulation de collections mixtes ou d'hiérarchies de classes.
Swift fournit trois formes de conversion de type :
as — conversion sécurisée (upcast), où le résultat est toujours connu à l'avance par le compilateuras? — conversion conditionnelle (optional) (downcast), retourne un optional si la conversion échoueas! — conversion forcée (forced), provoque un crash d'exécution si la conversion n'est pas possibleExemple de code :
class Animal {} class Dog: Animal { func bark() { print("Woof!") } } let animals: [Animal] = [Dog(), Animal()] for animal in animals { if let dog = animal as? Dog { dog.bark() // conversion sécurisée avec as? } else { print("Pas un chien !") } }
Caractéristiques clés :
Peut-on utiliser l'opérateur as! pour la conversion entre des types non reliés (par exemple, de String en Int) ? Qu'est-ce que cela donnerait ?
L'opérateur as! provoque toujours un crash d'exécution si les types sont incompatibles ou s'il n'existe pas d'héritage ou de hiérarchie de protocoles communs avec une implémentation. Cela est inacceptable pour une conversion entre des types fondamentalement différents.
Exemple de code :
let value: Any = "abc" let num = value as! Int // crash : Impossible de convertir une valeur de type 'String' en 'Int'
Que se passe-t-il si on utilise as? pour une conversion à un type sans aucune hiérarchie ?
as? retourne toujours nil si la conversion sécurisée est impossible — même si les types ne sont en aucun cas liés par héritage ou protocoles implémentés.
Exemple de code :
let value: Any = 5 if let str = value as? String { print(str) } else { print("Impossible de convertir en String") // Ce bloc sera exécuté }
Peut-on utiliser as pour des conversions vers le bas dans l'héritage de classes (downcasting) ?
Non. L'opérateur as est uniquement adapté pour l'upcast (par exemple, de Dog à Animal, ou lors de la conversion en un protocole implémenté). Pour les conversions vers le bas, on utilise toujours as? ou as!.
Exemple de code :
let animal: Animal = Dog() // let dog = animal as Dog // erreur de compilation let dog = animal as? Dog // méthode correcte
Dans un projet, il y avait une collection [Any], dans laquelle des chaînes et des nombres étaient accidentellement ajoutés. Pour traiter les données, le développeur écrivait à plusieurs endroits : let value = item as! String, entraînant un crash lorsque le nombre apparaissait dans le tableau.
Avantages :
Inconvénients :
Réécrit en utilisant des types associés d'enum :
enum Payload { case text(String); case number(Int) } let data: [Payload] = [.text("abc"), .number(1)]
Avantages :
Inconvénients :