Inheritance
Inheritance lets one class build on another, reusing its behaviour and
adding or refining it. It is the feature that most distinguishes classes
from the other named types in Swift. In this chapter you'll define base
classes, create subclasses, override what you inherit, reach back into a
superclass with super, and lock behaviour down with
final.
Note: Only classes inherit in Swift. Structures and enumerations are value types and have no inheritance — they gain shared behaviour through protocols and extensions instead.
Defining a base class
A base class is any class that does not inherit from another class. Swift classes don't descend from a universal root the way some languages require, so a class with no superclass simply stands on its own. Here is a base class describing any vehicle:
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "travelling at \(currentSpeed) km/h"
}
func makeNoise() {
// Does nothing — an arbitrary vehicle has no characteristic noise.
}
}
let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
// → Vehicle: travelling at 0.0 km/h
A stored property, a read-only computed property, and a method that subclasses can specialise.
Subclassing
Subclassing bases a new class on an existing one. The subclass inherits every characteristic of its superclass and can add more. You indicate the superclass after a colon:
class Bicycle: Vehicle {
var hasBasket = false
}
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0 // inherited from Vehicle
print("Bicycle: \(bicycle.description)")
// → Bicycle: travelling at 15.0 km/h
Subclasses can themselves be subclassed, building a chain of behaviour:
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
let tandem = Tandem()
tandem.hasBasket = true // from Bicycle
tandem.currentNumberOfPassengers = 2 // from Tandem
tandem.currentSpeed = 22.0 // from Vehicle
print("Tandem: \(tandem.description)")
// → Tandem: travelling at 22.0 km/h
Overriding
A subclass can provide its own implementation of an instance method,
type method, instance property, type property, or subscript that it
would otherwise inherit. This is called overriding. You
must prefix the new version with the override keyword;
Swift checks that a matching declaration genuinely exists in the
superclass, catching typos at compile time.
Overriding methods
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
let train = Train()
train.makeNoise()
// → Choo Choo
Overriding properties
You can override an inherited property to provide your own getter (and setter), or to add observers. You must restate the property's name and type so Swift can confirm it matches the superclass. A read-only inherited property may be overridden as read-write, but a read-write property cannot be narrowed to read-only.
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
let car = Car()
car.currentSpeed = 80.0
car.gear = 3
print("Car: \(car.description)")
// → Car: travelling at 80.0 km/h in gear 3
Overriding property observers
You can add willSet and didSet observers to an
inherited property regardless of how it was originally implemented. You
cannot, however, add observers to a constant stored property or to a
read-only computed property — neither can change after it is set.
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// → AutomaticCar: travelling at 35.0 km/h in gear 4
Accessing the superclass with super
Inside an override you can reach the version you replaced through the
super prefix. This is how you extend behaviour rather than
discard it:
super.someMethod()calls the superclass's method.super.somePropertyreads (or writes) the superclass's property.super[someIndex]reaches the superclass's subscript.
In the Car.description example above,
super.description produces the base sentence and the
subclass appends to it — composing behaviour instead of duplicating it.
Preventing overrides with final
Mark a member with final to forbid subclasses from
overriding it: final var, final func,
final class func, or final subscript. Marking
the whole class final prevents it from being subclassed at
all. Attempting to override anything final is a compile-time
error.
final class Spaceship: Vehicle {
final func launch() {
print("Lift off!")
}
}
// class Shuttle: Spaceship { } // ❌ error: cannot inherit from final class
Tip: Beyond preventing accidental overrides,
final helps the compiler devirtualise calls — it can dispatch
them directly instead of through a method table — which can make hot
code paths faster. Reach for it when a class or method is not designed to
be extended.
Wrapping up
Inheritance gives classes a way to share and refine behaviour: define a
base class, subclass it, override the parts you want to
change, call back into super to build on what you inherited,
and use final to seal off anything that shouldn't be
extended. Next we'll look at initialization — how
instances of these classes get their stored properties set up correctly,
including the special rules that apply when inheritance is in play.