Post

The final keyword in Swift

The final keyword in Swift

In Swift, we have a final keyword, which can prevent a class, method, property, or subscript from being overridden.

For example, if we want to prevent a class from being overridden. We can do like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final class BankAccount {
    let accountNumber: String
    private(set) var balance: Double

    init(accountNumber: String, initialBalance: Double) {
        self.accountNumber = accountNumber
        self.balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) {
        if amount <= balance {
            balance -= amount
        } else {
            print("Insufficient account balance")
        }
    }
}

class SavingAccount: BankAccount { } // Compile-time error

We can also only restrict the specific methods, properties, or subscripts from being overridden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class ColorPalette {
    // A final method that returns the hexadecimal color code for a given color name.
    final func hexColor(for name: String) -> String {
        let colorDict = [
            "red": "#FF0000",
            "green": "#00FF00",
            "blue": "#0000FF",
        ]
        return colorDict[name] ?? defaultColor
    }

    // A final computed property that returns the default color (white).
    final var defaultColor: String {
        return "#FFFFFF"
    }

    // A final subscript that returns a color name from a predefined list based on the index.
    final subscript(index: Int) -> String {
        let colors = ["red", "green", "blue", "yellow", "purple"]
        if index >= 0 && index < colors.count {
            return colors[index]
        } else {
            return "unknown"
        }
    }

    // A non-final computed property that can be overriden.
    var favoriteColor: String {
        return "red"
    }
}

let palette = ColorPalette()
print("Hex for red: \(palette.hexColor(for: "red"))")  // Expected Output: "#FF0000"
print("Default color: \(palette.defaultColor)")  // Expected Output: "#FFFFFF"
print("Color at index 2: \(palette[2])")  // Expected Output: "blue"
print("Color at index 5: \(palette[5])")  // Expected Output: "unknown"

class CustomColorPalette: ColorPalette {
    override func hexColor(for name: String) -> String { ... } // Compile-time error
    override var defaultColor: String { ... } // Compile-time error
    override subscript(index: Int) -> String { ... } // Compile-time error

    // Okay
    override var favoriteColor: String {
        return "green"
    }
}

Methods, properties, or subscripts that you add to a class extension can also be marked as final to prevent from being overridden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Vehicle {
    let make: String
    let model: String

    init(make: String, model: String) {
        self.make = make
        self.model = model
    }
}

extension Vehicle {
    @objc var fullDescription: String {
        "\(make) \(model)"
    }

    @objc final func honk() {
        print("Honk! Honk! - \(fullDescription)")
    }
}

class Car: Vehicle {
    // Okay
    override var fullDescription: String {
        return "Car: \(make) \(model)"
    }

    // Compile-time error
    override func honk() {
        print("Beep beep!")
    }
}

References

This post is licensed under CC BY 4.0 by the author.