Inheritance
A dog is an animal. Everything any animal has, a dog has too: a name and a voice, for example. But a dog's voice is special — "woof".
Now imagine a program with the classes Dog, Cat and Parrot, each with a name, an init and a speak() method. Writing the same thing three times means fixing one mistake three times too. A class can take the common part from another class, and then the common part is written once:
class Animal {
var name: String
init(name: String) {
self.name = name
}
func speak() -> String { "..." }
}
class Dog: Animal {
override func speak() -> String { "Woof" }
}
let rex = Dog(name: "Rex")
print(rex.name, rex.speak()) // Rex WoofLet's go through the program:
class Animalis the common class: it has the propertyname,init(name:)and the methodspeak().class Dog: Animal— after the colon comes the class from whichDogtakes everything common.Doghas neithernamenor its owninit, yetDog(name: "Rex")works:Doggot both fromAnimal.override func speak()replaces the voice ofAnimalwith its own, sorex.speak()returnsWoof.
Subclass and parent class
Inheritance is a way to declare a class based on another one: the new class gets all of its properties and methods. A class that inherits is called a subclass, and the class it inherits from is its parent. In the example, Dog is a subclass and Animal is its parent class.
A subclass can add its own properties and methods, or it can replace a parent's method with its own version. Such a replacement is called overriding, and it is marked with override.
Only classes have inheritance: a struct cannot inherit from another struct.
Here the dog picture falls short. A real dog can be an animal, a pet and a guard all at once, but a class in Swift has only one parent class.
The parent type and super
A subclass fits wherever a parent class is expected: a dog is an animal too. So a constant of type Animal can hold an instance of a subclass. And inside an overriding method, you can call the parent's version through super:
class Puppy: Dog {
override func speak() -> String {
super.speak() + "!"
}
}
let pet: Animal = Puppy(name: "Bim")
print(pet.speak()) // Woof!
print(type(of: pet)) // PuppyLet's see what happens here:
Puppyinherits fromDog, and so fromAnimaltoo.super.speak()calls the parent's version, that isDog's, and getsWoof.Puppyadds!to it.petis declared asAnimal, but the instance in it is aPuppy. The callpet.speak()runs thePuppyversion: the method is chosen by the class of the instance itself, not by the type of the constant.type(of: pet)confirms it and printsPuppy.
A super call runs exactly where it is written. The code of the overriding method before that call runs before the parent's version, and the code after it runs later.
Interviewers often ask whose version of a method a call runs when the constant is declared as the parent class. The answer: the version of the instance's own class.
Common mistake
A method with the same name as the parent's is written without override:
class Cat: Animal {
func speak() -> String { "Meow" } // ✗
}error: overriding declaration requires an 'override' keywordThe program does not compile: Animal already has the method speak(), and Swift requires you to say explicitly that you are replacing it. Add override before func.
The opposite mistake is caught too. If you write override on a method the parent does not have, for example with the typo speek(), the program does not compile. So override guards against two accidents: replacing a parent's method without noticing, and missing the parent's method you meant to replace.
Key points
class Dog: Animaldeclares a subclass: it gets all the properties and methods of its parent class.overridereplaces a parent's method, andsuper.speak()calls the parent's version where it is written.- A constant of the parent type can hold a subclass instance, and a method call runs the version of the instance's own class.
overrideis required on a method that replaces a parent's method and not allowed on a method the parent does not have.