Swift-Generics
Swift bietet Generics, damit Sie flexible und wiederverwendbare Funktionen und Typen schreiben können.
Die Swift-Standardbibliothek wurde mit generischem Code erstellt.
Swifts Array- und Dictionary-Typen sind generische Sammlungen.
Sie können ein Int-Array, ein String-Array oder sogar ein Array mit Daten eines beliebigen anderen Swift-Typs erstellen.
Das folgende Beispiel ist eine nicht generische Funktion exchange, mit der zwei Int-Werte vertauscht werden:
Beispiel
Die Ausgabe des obigen Programms ist:
交换前数据: 100 和 200 交换后数据: 200 和 100
Das obige Beispiel gilt nur für das Vertauschen von Variablen des ganzzahligen Typs Int. Wenn Sie zwei String-Werte oder Double-Werte vertauschen möchten, müssen Sie eine entsprechende Funktion erneut schreiben, wie z. B. swapTwoStrings(_:_:) und swapTwoDoubles(_:_:), wie unten gezeigt:
Funktionen zum Vertauschen von String- und Double-Werten
Aus dem obigen Code geht hervor, dass ihr Funktionscode derselbe ist, nur die Typen unterscheiden sich. In diesem Fall können wir Generics verwenden, um wiederholten Code zu vermeiden.
Generics verwenden einen Platzhalter-Typnamen (hier durch den Buchstaben T dargestellt), um den tatsächlichen Typnamen (wie Int, String oder Double) zu ersetzen.
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
Auf swapTwoValues folgt der Platzhalter-Typname (T), eingeschlossen in spitze Klammern (<T>). Diese spitzen Klammern teilen Swift mit, dass T ein Platzhalter-Typname innerhalb der Definition der Funktion swapTwoValues(_:_:) ist, sodass Swift nicht nach einem tatsächlichen Typ namens T sucht.
Das folgende Beispiel ist eine generische Funktion exchange, mit der zwei Int- und String-Werte vertauscht werden:
Beispiel
Die Ausgabe des obigen Programms ist:
交换前数据: 100 和 200 交换后数据: 200 和 100 交换前数据: A 和 B 交换后数据: B 和 A
Generische Typen
Mit Swift lassen sich eigene generische Typen definieren.
Benutzerdefinierte Klassen, Strukturen und Enums funktionieren mit jedem Typ, genau wie die Verwendung von Array und Dictionary.
Als Nächstes schreiben wir einen generischen Sammlungstyp namens Stack (Stapel). Ein Stack erlaubt nur, neue Elemente am Ende der Sammlung hinzuzufügen (als Push bezeichnet), und Elemente können ebenfalls nur vom Ende entfernt werden (als Pop bezeichnet).

Das Bild wird von links nach rechts wie folgt erklärt:
- Drei Werte befinden sich im Stack.
- Ein vierter Wert wird oben auf den Stack gepusht.
- Jetzt befinden sich vier Werte im Stack, wobei der zuletzt gepushte Wert oben liegt.
- Der Wert an der Spitze des Stacks wird entfernt oder gepoppt.
- Nach dem Entfernen eines Werts enthält der Stack jetzt wieder nur drei Werte.
Das folgende Beispiel ist eine nicht-generische Version des Stacks, wobei ein Int-Stack als Beispiel dient:
Int-Stack
Diese Struktur verwendet eine Array-Eigenschaft namens items im Stack, um Werte zu speichern. Stack bietet zwei Methoden: push(_:) und pop(), mit denen Werte auf den Stack gelegt und Werte vom Stack entfernt werden. Diese Methoden sind als mutating markiert, weil sie das items-Array der Struktur ändern müssen.
Die obige IntStack-Struktur kann nur für den Typ Int verwendet werden. Sie können jedoch eine generische Stack-Struktur definieren, sodass sie Werte beliebigen Typs verarbeiten kann.
Nachfolgend sehen Sie die generische Version desselben Codes:
Generischer Stack
Das Ausführungsergebnis der Instanz ist:
字符串元素入栈: ["google", "example"] 出栈元素: example 整数元素入栈: [1, 2]
Stack ist im Wesentlichen dasselbe wie IntStack, wobei der Platzhalter-Typparameter Element den tatsächlichen Typ Int ersetzt.
Im obigen Beispiel wird Element als Platzhalter an den folgenden drei Stellen verwendet:
- Erstellen deritems-Eigenschaft, wobei ein leeres Array vom TypElementzum Initialisieren verwendet wird.
- Geben Sie an:push(_:)Der einzige Parameter der Methodeitemmuss vom TypElement sein.Typ.
- Geben Sie an:pop()Der Rückgabetyp der Methode mussElementTyp sein.
Erweitern eines generischen Typs
Wenn Sie einen generischen Typ erweitern (mit dem Schlüsselwort extension), müssen Sie in der Erweiterungsdefinition keine Typparameterliste angeben. Praktischerweise ist die in der ursprünglichen Typdefinition deklarierte Typparameterliste in der Erweiterung verfügbar, und die Parameternamen des ursprünglichen Typs werden als Verweise auf die Typparameter in der ursprünglichen Definition verwendet.
Das folgende Beispiel erweitert den generischen Typ Stack, indem es eine schreibgeschützte berechnete Eigenschaft namens topItem hinzufügt, die das Element oben auf dem aktuellen Stack zurückgibt, ohne es vom Stack zu entfernen:Generika
Im Beispiel gibt die Eigenschaft topItem einen optionalen Wert vom Typ Element zurück. Wenn der Stack leer ist, gibt topItem nil zurück; wenn der Stack nicht leer ist, gibt topItem das letzte Element im items-Array zurück.
Die Ausgabe des obigen Programms ist:
字符串元素入栈: 栈中的顶部元素是:example. ["google", "example"]
Man kann auch einen assoziierten Typ angeben, indem man einen vorhandenen Typ erweitert.
Zum Beispiel bietet der Array-Typ von Swift bereits eine append(_:)-Methode, eine count-Eigenschaft und einen Subscript, der einen Int-Indexwert akzeptiert, um seine Elemente abzurufen. Diese drei Merkmale erfüllen alle die Anforderungen des Container-Protokolls, sodass man Array einfach erweitern kann, indem man deklariert, dass Array das Protokoll übernimmt.
Das folgende Beispiel erstellt nur eine leere Erweiterung:
extension Array: Container {}
Typbeschränkungen
Typbeschränkungen legen fest, dass ein Typparameter von einer angegebenen Klasse erben oder einem bestimmten Protokoll bzw. einer Protokollkomposition entsprechen muss.
Syntax für Typbeschränkungen
Sie können eine Typbeschränkung durch einen Doppelpunkt getrennt nach einem Typparameternamen als Teil der Typparameterkette schreiben. Die grundlegende Syntax für solche Typbeschränkungen bei generischen Funktionen ist unten dargestellt (identisch mit der Syntax für generische Typen):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
Diese Funktion hat zwei Typparameter. Der erste Typparameter, T, hat eine Typbeschränkung, die verlangt, dass T eine Unterklasse von SomeClass ist; der zweite Typparameter, U, hat eine Typbeschränkung, die verlangt, dass U dem Protokoll SomeProtocol entspricht.
Beispiel
Generika
Der Index-Subscript beginnt bei 0.
Die Ausgabe des obigen Programms ist:
example 的索引为 3
Assoziierte Typen
In Swift wird das Schlüsselwort associatedtype verwendet, um assoziierte Typinstanzen einzurichten.
Das folgende Beispiel definiert ein Container-Protokoll, das einen assoziierten Typ ItemType definiert.
Das Container-Protokoll gibt nur drei Merkmale vor, die jeder Typ, der dem Container-Protokoll entspricht, bereitstellen muss. Ein Typ, der dem Protokoll entspricht, kann zusätzlich weitere Merkmale bereitstellen und dabei diese drei Bedingungen erfüllen.
// 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)
Die Ausgabe des obigen Programms ist:
["google", "example", "taobao"] 3
Where-Klauseln
Typ-Constraints können sicherstellen, dass ein Typ den Einschränkungen entspricht, die durch eine generische Funktion oder Klasse definiert werden.
Sie können Einschränkungen für Parameter in der Parameterliste durch eine where-Klausel definieren.
Du kannst eine where-Anweisung unmittelbar nach der Typparameterliste schreiben, gefolgt von einer oder mehreren Einschränkungen für zugehörige Typen und/oder einer oder mehreren Gleichheitsbeziehungen zwischen Typen und zugehörigen Typen.
Beispiel
Das folgende Beispiel definiert eine generische Funktion namens allItemsMatch, mit der überprüft wird, ob zwei Container-Instanzen dieselben Elemente in derselben Reihenfolge enthalten.
Wenn alle Elemente übereinstimmen, gibt sie true zurück; andernfalls gibt sie false zurück.
Generika
Die Ausgabe der obigen Programmausführung ist:
Match all elementsAndere Erweiterungen