Menu
Coddy logo textTech

Fehlschlagende Initialisierer

Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 20 von 57.

Manche Werte können nicht aus jeder Eingabe erstellt werden. Ein fehlerschlagender Initialisierer, geschrieben als init?, gibt bei einer ungültigen Eingabe nil statt einer Instanz zurück, sodass niemals eine ungültige Instanz existieren kann:

struct Percentage {
    let value: Int

    init?(_ value: Int) {
        guard value >= 0, value <= 100 else { return nil }
        self.value = value
    }
}

print(Percentage(42)?.value ?? -1)
print(Percentage(150)?.value ?? -1)

Ausgabe:

42
-1

Das Ergebnis eines fehlbaren Initialisierers ist optional, daher muss der Aufrufer es entpacken, normalerweise mit if let oder guard let:

struct Email {
    let address: String

    init?(_ text: String) {
        let parts = text.split(separator: "@")
        guard parts.count == 2, parts[1].contains(".") else { return nil }
        address = text.lowercased()
    }
}

for text in ["Ada@Mail.com", "not-an-email"] {
    if let email = Email(text) {
        print("valid: \(email.address)")
    } else {
        print("invalid: \(text)")
    }
}

Ausgabe:

valid: ada@mail.com
invalid: not-an-email

Die eigenen Typen von Swift verwenden dieselbe Idee: Int("42") und Double("x") sind fehlbare Initialisierer, ebenso wie der init(rawValue:) einer Aufzählung. Ein fehlbarer Initialisierer kann diese aufrufen und den Fehler weitergeben:

struct Coordinate {
    let lat: Double
    let lon: Double

    init?(text: String) {
        let parts = text.split(separator: ",")
        guard parts.count == 2,
              let lat = Double(parts[0]), let lon = Double(parts[1]),
              abs(lat) <= 90, abs(lon) <= 180 else { return nil }
        self.lat = lat
        self.lon = lon
    }
}

print(Coordinate(text: "51.5,-0.1") != nil)
print(Coordinate(text: "95,10") != nil)

Ausgabe:

true
false

Verwende init?, wenn ein fehlender Wert ausreichend Information liefert. Verwende einen Initialisierer, der einen Fehler auslöst, init(...) throws, wenn der Aufrufer erfahren soll, welche Regel fehlgeschlagen ist:

struct Username {
    let text: String
    init(_ text: String) throws {
        guard text.count >= 3 else { throw NameError.tooShort }
        guard text.allSatisfy({ $0.isLetter }) else { throw NameError.badCharacters }
        self.text = text
    }
}
challenge icon

Aufgabe

Einfach

Vervollständige die Struktur Rgb, die eine Farbe als red, green und blue im Bereich von 0 bis 255 speichert. Ihr fehlerschlagender Initialisierer init?(hex:) liest Text wie #FF8000: Er gibt nil zurück, sofern der Text nicht aus # gefolgt von genau sechs Hexadezimalziffern besteht. Verwende Int(text, radix: 16) für jedes Paar. Füge ein berechnetes label hinzu, das rgb(255, 128, 0) zurückgibt.

Der bereitgestellte Code liest eine Farbe pro Zeile ein und gibt entweder das Label oder invalid: text aus.

Dein Code gehört in Rgb.swift. main.swift enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

// Mitgelieferter Ein-/Ausgabecode: belasse ihn unverändert
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    if let color = Rgb(hex: line) {
        print(color.label)
    } else {
        print("invalid: \(line)")
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Swift-Playground