TL ; DR:
Put it all together: a parent class with a placeholder method, three concrete subclasses, and a polymorphic loop that dispatches by runtime type. This is the shape of nearly every real OOP design.
class Animal {
var name: String
init(_ name: String) { self.name = name }
func sound() -> String { return "" } // each subclass picks
func speak() { // shared by all
print("\(name): \(sound())")
}
}
// Dog, Cat, Bird each override sound()
// let zoo: [Animal] = [Dog("Rex"), ...]
// for a in zoo { a.speak() }
Full lesson:
This problem stitches together everything you've learned in this chapter:
- Inheritance — three concrete classes share a common parent.
- Initializers — each subclass inherits the parent's
init(_ name:), soDog("Rex")stores the name. - Abstract-like methods — Swift has no
abstractkeyword, so the parent'ssound()is a placeholder that returns""; each child overrides it with its own sound. - Shared behavior — the parent has a concrete
speak()that callssound(); every subclass inherits it for free. - Polymorphism — a collection typed as
Animalholds a Dog, a Cat, and a Bird;speak()dispatches to the rightsound()at runtime.
The shape of this code is the shape of nearly every real OOP design you'll see — a small contract at the top (the parent class), concrete variants below, and the rest of the program written against the abstract type. New variants get added without touching the consumer code.
Your task
The Animal class is already defined, with a concrete speak() that calls the placeholder sound(). Add three subclasses — Dog (returns "Woof"), Cat (returns "Meow"), and Bird (returns "Tweet"). Then in mainFunction, build a polymorphic collection holding one of each (Rex, Whiskers, Tweety in that order) and loop through it calling speak().
Expected output:
Rex: Woof
Whiskers: Meow
Tweety: TweetFree plan: 30 tutor messages. PRO is unlimited.
