Menu
Coddy logo textTech

final and Hierarchies

Part of the Object Oriented Programming section of Coddy's Swift journey. Lesson 25 of 57.

final stops further overriding. A final func cannot be overridden in any subclass, and a final class cannot be subclassed at all:

class Account {
    final func id() -> String { "ACC" }   // cannot be overridden
}

final class Receipt { }                  // cannot be subclassed
// class GiftReceipt: Receipt { }        // error: inheritance from a final class

Mark a method final when subclasses must not change it, for example a method that enforces a rule. Other methods can still be overridden to customise details:

class Checkout {
    final func total(_ prices: [Int]) -> Int {
        let sum = prices.reduce(0, +)
        return sum - discount(sum)
    }
    func discount(_ sum: Int) -> Int { 0 }
}

class SaleCheckout: Checkout {
    override func discount(_ sum: Int) -> Int { sum / 10 }
}

print(Checkout().total([50, 50]))
print(SaleCheckout().total([50, 50]))

Output:

100
90

Hierarchies can have several levels. A method is looked up from the object's own class upwards, and the first implementation found runs:

class Animal {
    func move() -> String { "moves" }
    func eat() -> String { "eats" }
}
class Bird: Animal {
    override func move() -> String { "flies" }
}
class Penguin: Bird {
    override func move() -> String { "swims" }
}

let p = Penguin()
print(p.move())
print(p.eat())

Output:

swims
eats

Keep hierarchies shallow. Deep chains are hard to follow, because the behaviour of one object is spread over many classes. When types share behaviour without an is-a relation, protocols (next chapter) are usually the better tool.

// deep and fragile
class A { }
class B: A { }
class C: B { }
class D: C { }      // what does D inherit, and from where?
challenge icon

Challenge

Easy

Discount has a final method apply(to:) that returns the price minus amount(for:) but never below 0, and amount(for:) returns 0. Declare PercentOff (stored percent, amount = price times percent / 100) and FixedOff (stored value, amount = value), each with an initializer and an override of amount(for:). apply(to:) itself stays in Discount.

The supplied code reads the price and then discounts p,10 or f,300, applies each to the price and prints 1000 -> 900.

Your code goes in Discount.swift, PercentOff.swift and FixedOff.swift. main.swift holds the supplied input/output code and cannot be edited.

Try it yourself

// Supplied input/output code: keep it as it is
var input: [String] = []
while let line = readLine() { input.append(line) }
let price = Int(input[0])!
for line in input.dropFirst() {
    let p = line.split(separator: ",").map(String.init)
    let d: Discount = p[0] == "p" ? PercentOff(percent: Int(p[1])!) : FixedOff(value: Int(p[1])!)
    print("\(price) -> \(d.apply(to: price))")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Swift playground