Menu
CoddyTech

Hàm khởi tạo có thể thất bại

Thuộc phần Lập trình hướng đối tượng trong hành trình Swift của Coddy. Bài học 20 trên 57.

Một số giá trị không thể được tạo từ mọi đầu vào. Một hàm khởi tạo có thể thất bại, được viết là init?, trả về nil thay vì một đối tượng khi đầu vào không hợp lệ, vì vậy sẽ không bao giờ tồn tại đối tượng không hợp lệ nào:

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)

Đầu ra:

42
-1

Kết quả của hàm khởi tạo có thể thất bại là một giá trị tùy chọn, vì vậy bên gọi phải mở gói giá trị đó, thường dùng if let hoặc 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)")
    }
}

Đầu ra:

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

Các kiểu dữ liệu của Swift cũng dùng cùng ý tưởng này: Int("42") và Double("x") là các hàm khởi tạo có thể thất bại, và init(rawValue:) của enum cũng vậy. Hàm khởi tạo có thể thất bại có thể gọi những hàm này và chuyển tiếp lỗi:

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)

Đầu ra:

true
false

Dùng init? khi chỉ cần biết giá trị bị thiếu. Dùng hàm khởi tạo có thể ném ngoại lệ, init(...) throws, khi bên gọi cần biết quy tắc nào đã không được đáp ứng:

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

Thử thách

Dễ

Hoàn thiện struct Rgb, struct này lưu màu dưới dạng red, green và blue với giá trị từ 0 đến 255. Hàm khởi tạo có thể thất bại init?(hex:) đọc văn bản như #FF8000: hàm trả về nil trừ khi văn bản là # theo sau bởi đúng sáu chữ số thập lục phân. Dùng Int(text, radix: 16) cho từng cặp. Thêm thuộc tính được tính toán label trả về rgb(255, 128, 0).

Code được cung cấp đọc một màu trên mỗi dòng và in nhãn hoặc invalid: text.

Code của bạn nằm trong Rgb.swift. main.swift chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

// Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Playground Swift