Génériques Swift
Swift fournit des génériques pour que vous puissiez écrire des fonctions et des types flexibles et réutilisables.
La bibliothèque standard Swift est construite avec du code générique.
Les types Array et Dictionary de Swift sont des collections génériques.
Vous pouvez créer un tableau d'Int, un tableau de String, ou même un tableau de données de tout autre type Swift.
L'exemple suivant est une fonction d'échange non générique utilisée pour échanger deux valeurs Int :
Exemple
La sortie d'exécution du programme ci-dessus est :
交换前数据: 100 和 200 交换后数据: 200 和 100
L'exemple ci-dessus ne s'applique qu'à l'échange de variables de type entier Int. Si vous voulez échanger deux valeurs String ou Double, vous devez écrire à nouveau une fonction correspondante, telle que swapTwoStrings(_:_:) et swapTwoDoubles(_:_:), comme montré ci-dessous :
Fonctions d'échange de valeurs String et Double
D'après le code ci-dessus, leur code fonctionnel est le même, seuls les types sont différents. Dans ce cas, nous pouvons utiliser des génériques pour éviter d'écrire du code répétitif.
Les génériques utilisent un nom de type de substitution (représenté ici par la lettre T) pour remplacer le nom de type réel (tel que Int, String ou Double).
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
swapTwoValues est suivi du nom de type de substitution (T), placé entre chevrons (<T>). Ces chevrons indiquent à Swift que T est un nom de type de substitution dans la définition de la fonction swapTwoValues(_:_:), ainsi Swift ne cherchera pas un type réel nommé T.
L'exemple suivant est une fonction générique exchange utilisée pour échanger deux valeurs Int et String :
Exemple
La sortie d'exécution du programme ci-dessus est :
交换前数据: 100 和 200 交换后数据: 200 和 100 交换前数据: A 和 B 交换后数据: B 和 A
Types génériques
Swift vous permet de définir vos propres types génériques.
Les classes, structures et énumérations personnalisées fonctionnent avec n'importe quel type, tout comme l'utilisation de Array et Dictionary.
Ensuite, nous allons écrire un type de collection générique nommé Stack (pile). Une pile permet uniquement d'ajouter de nouveaux éléments à la fin de la collection (opération appelée empilement), et les éléments ne peuvent être retirés que de la fin également (opération appelée dépilement).

L'image est expliquée de gauche à droite comme suit :
- Trois valeurs se trouvent dans la pile.
- Une quatrième valeur est empilée au sommet de la pile.
- Maintenant, il y a quatre valeurs dans la pile, avec la valeur la plus récemment empilée au sommet.
- La valeur au sommet de la pile est retirée, ou dépilée.
- Après avoir retiré une valeur, la pile ne contient à nouveau plus que trois valeurs.
L'exemple suivant est une version non générique de la pile, en prenant une pile d'Int comme exemple :
Pile d'Int
Cette structure utilise une propriété Array nommée items dans la pile pour stocker des valeurs. Stack fournit deux méthodes : push(_:) et pop(), utilisées pour pousser des valeurs dans la pile et retirer des valeurs de la pile. Ces méthodes sont marquées comme mutating car elles doivent modifier le tableau items de la structure.
La structure IntStack ci-dessus ne peut être utilisée que pour le type Int. Cependant, vous pouvez définir une structure Stack générique afin qu'elle puisse gérer des valeurs de n'importe quel type.
Ci-dessous se trouve la version générique du même code :
Stack générique
Le résultat d'exécution de l'instance est :
字符串元素入栈: ["google", "example"] 出栈元素: example 整数元素入栈: [1, 2]
Stack est essentiellement identique à IntStack, avec le paramètre de type de remplacement Element qui remplace le type Int réel.
Dans l'exemple ci-dessus, Element est utilisé comme espace réservé aux trois emplacements suivants :
- Création de la propriétéitems, en utilisant un tableau vide du typeElementpour l'initialiser.
- Spécifiez pourpush(_:)que le seul paramètre de la méthodeitemdoit être de typeElementtype.
- Spécifiez pourpop()que le type de valeur de retour de la méthode doit être de typeElement.
Extension d’un type générique
Lorsque vous étendez un type générique (à l’aide du mot-clé extension), vous n’avez pas besoin de fournir une liste de paramètres de type dans la définition de l’extension. Plus commodément, la liste des paramètres de type déclarée dans la définition du type d’origine est disponible dans l’extension, et les noms des paramètres du type d’origine sont utilisés comme références aux paramètres de type dans la définition d’origine.
L’exemple suivant étend le type générique Stack, en ajoutant une propriété calculée en lecture seule nommée topItem, qui renvoie l’élément au sommet de la pile actuelle sans le retirer de la pile :Génériques
Dans l'exemple, la propriété topItem renvoie une valeur optionnelle de type Element. Lorsque la pile est vide, topItem renvoie nil ; lorsqu'elle n'est pas vide, topItem renvoie le dernier élément du tableau items.
La sortie d'exécution du programme ci-dessus est :
字符串元素入栈: 栈中的顶部元素是:example. ["google", "example"]
Nous pouvons également spécifier un type associé en étendant un type existant.
Par exemple, le type Array de Swift fournit déjà une méthode append(_:), une propriété count et un indice qui accepte une valeur d'index Int pour récupérer ses éléments. Ces trois fonctionnalités répondent toutes aux exigences du protocole Container, donc vous pouvez étendre Array simplement en déclarant qu'Array adopte le protocole.
L'exemple suivant crée uniquement une extension vide :
extension Array: Container {}
Contraintes de type
Les contraintes de type spécifient qu'un paramètre de type doit hériter d'une classe spécifiée, ou se conformer à un protocole particulier ou à une composition de protocoles.
Syntaxe des contraintes de type
Vous pouvez écrire une contrainte de type après le nom d'un paramètre de type, séparée par deux-points, dans le cadre de la chaîne de paramètres de type. La syntaxe de base de ces contraintes de type sur les fonctions génériques est présentée ci-dessous (identique à la syntaxe des types génériques) :
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
Cette fonction a deux paramètres de type. Le premier paramètre de type, T, a une contrainte de type exigeant que T soit une sous-classe de SomeClass ; le second paramètre de type, U, a une contrainte de type exigeant que U se conforme au protocole SomeProtocol.
Exemple
Génériques
L'indice commence à 0.
La sortie d'exécution du programme ci-dessus est :
example 的索引为 3
Types associés
En Swift, le mot-clé associatedtype est utilisé pour définir des instances de type associé.
L’exemple suivant définit un protocole Container, qui définit un type associé ItemType.
Le protocole Container ne spécifie que trois fonctionnalités que tout type conforme au protocole Container doit fournir. Un type qui se conforme au protocole peut également fournir des fonctionnalités supplémentaires tout en satisfaisant ces trois conditions.
// Container 协议
protocol Container {
associatedtype ItemType
// 添加一个新元素到容器里
mutating func append(_ item: ItemType)
// 获取容器中元素的数
var count: Int { get }
// 通过索引值类型为 Int 的下标检索到容器中的每一个元素
subscript(i: Int) -> ItemType { get }
}
// Stack 结构体遵从 Container 协议
struct Stack<Element>: Container {
// Stack<Element> 的原始实现部分
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 协议的实现部分
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
var tos = Stack<String>()
tos.push("google")
tos.push("example")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素个数
print( tos.count)
La sortie d'exécution du programme ci-dessus est :
["google", "example", "taobao"] 3
Clauses Where
Les contraintes de type peuvent garantir qu’un type se conforme aux contraintes définies par une fonction ou une classe générique.
Vous pouvez définir des contraintes sur les paramètres dans la liste de paramètres à l’aide d’une clause where.
Vous pouvez écrire une instruction where immédiatement après la liste de paramètres de type, suivie d’une ou plusieurs contraintes sur des types associés et/ou d’une ou plusieurs relations d’égalité entre des types et des types associés.
Exemple
L’exemple suivant définit une fonction générique nommée allItemsMatch, utilisée pour vérifier si deux instances de Container contiennent les mêmes éléments dans le même ordre.
Si tous les éléments correspondent, elle renvoie true ; sinon, elle renvoie false.
Génériques
La sortie de l'exécution du programme ci-dessus est :
Match all elementsAutres extensions