Im Swift-Programmierungs语言 löst der Typumwandlungsmechanismus die Aufgabe der Überprüfung und Umwandlung von Werten von einem Typ in einen anderen zur Laufzeit. Seine Wurzeln liegen in der statischen Typisierung und Vererbung: In Swift können gleichzeitig Werttypen (struct/enum) und Referenztypen (class/protocol) verwendet werden. Probleme entstehen, wenn ein Objekt oder Wert in einer Variable des Typs Any oder eines Protokolls landet – und es erforderlich ist, zu überprüfen, ob es in einen anderen (häufig spezifischeren) Typ umgewandelt werden kann, ohne dass es zu einem Absturz der Anwendung kommt.
Eine sichere Typumwandlung ermöglicht es, die Vorteile der Typensicherheit, dynamischen Polymorphie zu nutzen und Fehler im Umgang mit gemischten Sammlungen oder Klassenhierarchien zu vermeiden.
Swift bietet drei Formen der Typumwandlung:
as — sichere Umwandlung (upcast), bei der das Ergebnis dem Compiler immer im Voraus bekannt istas? — bedingte (optional) Umwandlung (downcast), gibt optional zurück, wenn die Umwandlung nicht möglich istas! — erzwungene (forced) Umwandlung, führt zu einem Laufzeitabsturz, wenn die Umwandlung nicht möglich istBeispielcode:
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() // sichere Umwandlung mit as? } else { print("Nicht ein Hund!") } }
Hauptmerkmale:
Kann der Operator as! für die Umwandlung zwischen nicht verwandten Typen (z. B. von String nach Int) verwendet werden? Wie wird das enden?
Der Operator as! führt immer zu einem Laufzeitabsturz, wenn die Typen inkompatibel sind oder zwischen ihnen keine Vererbung oder gemeinsames Protokollmit einer Implementierung besteht. Dies ist nicht erlaubt für die Umwandlung zwischen grundlegend verschiedenen Typen.
Beispielcode:
let value: Any = "abc" let num = value as! Int // absturz: konnte Wert vom Typ 'String' nicht in 'Int' umwandeln
Was passiert, wenn man as? bei der Umwandlung in einen Typ verwendet, mit dem es keine Hierarchie gibt?
as? gibt immer nil zurück, wenn eine sichere Umwandlung nicht möglich ist — selbst wenn die Typen nicht durch Vererbung oder implementierte Protokolle verbunden sind.
Beispielcode:
let value: Any = 5 if let str = value as? String { print(str) } else { print("Kann nicht in String umwandeln") // Diese Zweig wird ausgeführt }
Kann as für die Abwärtsumwandlung in Klassenhierarchien (downcasting) verwendet werden?
Nein. Der Operator as ist nur für upcast geeignet (z. B. von Dog in Animal oder bei der Umwandlung in ein implementiertes Protokoll). Für Abwärtsumwandlungen wird immer as? oder as! verwendet.
Beispielcode:
let animal: Animal = Dog() // let dog = animal as Dog // Compiler-Fehler let dog = animal as? Dog // Richtiger Weg
In einem Projekt gab es eine Sammlung [Any], in die versehentlich Strings und Zahlen eingefügt wurden. Um die Daten zu verarbeiten, schrieb der Entwickler an mehreren Stellen: let value = item as! String, was zu einem Absturz führte, wenn eine Zahl im Array erschien.
Vorteile:
Nachteile:
Dies wurde mit assoziierten Typen in enums umgeschrieben:
enum Payload { case text(String); case number(Int) } let data: [Payload] = [.text("abc"), .number(1)]
Vorteile:
Nachteile: