Inheritance

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

A class can inherit methods, properties, subscripts and other characteristics from another class. Inheritance is what most fundamentally distinguishes classes from the other named types — structs and enums cannot inherit from another struct or enum at all.

Base Classes and Subclassing

class Animal {
    let name: String
    var energy = 100

    init(name: String) {
        self.name = name
    }

    func makeSound() -> String {
        "..."
    }

    var summary: String {
        "\(name) says \(makeSound())"
    }
}

class Dog: Animal {
    var breed: String

    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)              // a subclass's own storage is set first, then the superclass's
    }
}

let generic = Animal(name: "Generic Animal")
let rex = Dog(name: "Rex", breed: "Labrador")
print(rex.name, rex.breed)                   // Dog inherits `name` and `energy` from Animal for free

A class with no explicit superclass (Animal above) is a base class — Swift classes do not implicitly inherit from a universal root class the way Objective-C classes inherit from NSObject. Writing : Animal after Dog makes it a subclass of Animal, inheriting every method, property and subscript Animal declares; a subclass may add its own storage and behavior on top, and a subclass initializer that adds stored properties must call super.init(…​) to let the superclass finish initializing its own storage (see Initialization and Deinitialization for the full delegation and safety-check rules this feeds into).

Overriding Methods, Properties and Observers, and Calling super

class Cat: Animal {
    override func makeSound() -> String {                     // `override` replaces the inherited implementation
        "Meow"
    }

    override var summary: String {                             // computed properties can be overridden too
        super.summary + " (a cat, obviously)"                   // `super.` reaches the superclass's version
    }

    override var energy: Int {
        didSet {                                                // adding an observer to an *inherited* property
            if energy < oldValue {
                print("\(name) used some energy, now at \(energy)")
            }
        }
    }
}

let whiskers = Cat(name: "Whiskers")
print(whiskers.summary)     // Whiskers says Meow (a cat, obviously)
whiskers.energy -= 10        // Whiskers used some energy, now at 90

override is required whenever a subclass redeclares an inherited method, subscript, or property (the compiler rejects an accidental redeclaration missing the keyword, and rejects override on a member that overrides nothing). A property override may change nothing but add observers to an inherited stored or computed property, as Cat.energy does above — the inherited implementation still runs first, and the override’s willSet/didSet runs in addition to it. super.<member> reaches the superclass’s own implementation of a method, computed property, or subscript from inside an override — most commonly to extend rather than fully replace the inherited behavior, as summary does above.

final, Dynamic Dispatch and Polymorphism

class Base {
    final func cannotBeOverridden() -> String { "fixed behavior" }   // `final` on a member: no subclass may override it
}

final class Sealed: Base {}    // `final` on the class itself: no subclass of Sealed may ever be declared

let animals: [Animal] = [generic, rex, whiskers]
for animal in animals {
    print(animal.summary)      // each call dispatches to the *runtime* type's implementation, not `Animal`'s
}

Marking a method, property or subscript final forbids any subclass from overriding it; marking the class itself final forbids subclassing it at all — both are optimization and API-design tools: a final member can be dispatched statically instead of dynamically, and a sealed hierarchy can’t be extended in ways its author didn’t anticipate. Calling an overridable member through a superclass-typed reference or array element (animal.summary above) is dynamic dispatch: Swift looks up the actual runtime type of the instance and calls its implementation, which is what lets a loop over [Animal] produce `Dog’s, `Cat’s and `Animal’s own behavior side by side — polymorphism.

When to Subclass vs. Compose or Use Protocols

classDiagram class Animal { +name: String +energy: Int +makeSound() String +summary: String } class Dog { +breed: String +makeSound() String } class Cat { +makeSound() String +summary: String } Animal <|-- Dog Animal <|-- Cat

The Animal/Dog/Cat hierarchy above is small enough to be harmless, but it already shows inheritance’s known problems at scale: a subclass inherits everything from its superclass whether it wants it or not (a RobotDog would still inherit energy, which makes no sense for a machine); Swift classes support only single inheritance, so once a type needs to combine "makes a sound" with "can be drawn on screen" and "can be encoded to JSON", a class hierarchy alone cannot express all three without one of them living awkwardly inside another; and a change to a base class’s implementation can silently break a distant subclass that depended on the old behavior (the fragile base class problem) — worse the deeper the hierarchy grows.

Composition (a type holding another type as a property and delegating to it, rather than inheriting from it) and protocols (declaring a set of requirements any type — class, struct, or enum — can adopt, with no implementation and no single-inheritance limit) sidestep all three: prefer composing or adopting a protocol over subclassing whenever the relationship is "has a" or "can do", and reserve subclassing for a genuine "is a" relationship where sharing a base class’s stored state and initializers is actually wanted. This is the motivation behind protocol-oriented programming, covered in full in Protocols.

See Also