Skip to content
Theory · Classes · lesson 5 of 10

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:

SWIFT
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 Woof

Let's go through the program:

  1. class Animal is the common class: it has the property name, init(name:) and the method speak().
  2. class Dog: Animal — after the colon comes the class from which Dog takes everything common.
  3. Dog has neither name nor its own init, yet Dog(name: "Rex") works: Dog got both from Animal.
  4. override func speak() replaces the voice of Animal with its own, so rex.speak() returns Woof.

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:

SWIFT
class Puppy: Dog {
    override func speak() -> String {
        super.speak() + "!"
    }
}

let pet: Animal = Puppy(name: "Bim")
print(pet.speak()) // Woof!
print(type(of: pet)) // Puppy

Let's see what happens here:

  1. Puppy inherits from Dog, and so from Animal too.
  2. super.speak() calls the parent's version, that is Dog's, and gets Woof. Puppy adds ! to it.
  3. pet is declared as Animal, but the instance in it is a Puppy. The call pet.speak() runs the Puppy version: the method is chosen by the class of the instance itself, not by the type of the constant.
  4. type(of: pet) confirms it and prints Puppy.

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:

SWIFT
class Cat: Animal {
    func speak() -> String { "Meow" } // ✗
}
TEXT
error: overriding declaration requires an 'override' keyword

The 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: Animal declares a subclass: it gets all the properties and methods of its parent class.
  • override replaces a parent's method, and super.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.
  • override is required on a method that replaces a parent's method and not allowed on a method the parent does not have.