Files
gh-kylehughes-the-unofficia…/skills/programming-swift/LanguageGuide/Inheritance.md
2025-11-30 08:36:15 +08:00

618 lines
18 KiB
Markdown

# Inheritance
Subclass to add or override functionality.
A class can *inherit* methods, properties, and other characteristics
from another class.
When one class inherits from another,
the inheriting class is known as a *subclass*,
and the class it inherits from is known as its *superclass*.
Inheritance is a fundamental behavior that differentiates classes
from other types in Swift.
Classes in Swift can call and access
methods, properties, and subscripts belonging to their superclass
and can provide their own overriding versions of those methods, properties, and subscripts
to refine or modify their behavior.
Swift helps to ensure your overrides are correct
by checking that the override definition has a matching superclass definition.
Classes can also add property observers to inherited properties
in order to be notified when the value of a property changes.
Property observers can be added to any property,
regardless of whether it was originally defined as a stored or computed property.
## Defining a Base Class
Any class that doesn't inherit from another class is known as a *base class*.
> Note: Swift classes don't inherit from a universal base class.
> Classes you define without specifying a superclass
> automatically become base classes for you to build upon.
The example below defines a base class called `Vehicle`.
This base class defines a stored property called `currentSpeed`,
with a default value of `0.0` (inferring a property type of `Double`).
The `currentSpeed` property's value is used by
a read-only computed `String` property called `description`
to create a description of the vehicle.
The `Vehicle` base class also defines a method called `makeNoise`.
This method doesn't actually do anything for a base `Vehicle` instance,
but will be customized by subclasses of `Vehicle` later on:
```swift
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
```
-->
You create a new instance of `Vehicle` with *initializer syntax*,
which is written as a type name followed by empty parentheses:
```swift
let someVehicle = Vehicle()
```
<!--
- test: `inheritance`
```swifttest
-> let someVehicle = Vehicle()
```
-->
Having created a new `Vehicle` instance,
you can access its `description` property to print
a human-readable description of the vehicle's current speed:
```swift
print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> print("Vehicle: \(someVehicle.description)")
</ Vehicle: traveling at 0.0 miles per hour
```
-->
The `Vehicle` class defines common characteristics for an arbitrary vehicle,
but isn't much use in itself.
To make it more useful,
you need to refine it to describe more specific kinds of vehicles.
## Subclassing
*Subclassing* is the act of basing a new class on an existing class.
The subclass inherits characteristics from the existing class, which you can then refine.
You can also add new characteristics to the subclass.
To indicate that a subclass has a superclass,
write the subclass name before the superclass name,
separated by a colon:
```swift
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
```
<!--
- test: `protocolSyntax`
```swifttest
>> class SomeSuperclass {}
-> class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
```
-->
The following example defines a subclass called `Bicycle`,
with a superclass of `Vehicle`:
```swift
class Bicycle: Vehicle {
var hasBasket = false
}
```
<!--
- test: `inheritance`
```swifttest
-> class Bicycle: Vehicle {
var hasBasket = false
}
```
-->
The new `Bicycle` class automatically gains all of the characteristics of `Vehicle`,
such as its `currentSpeed` and `description` properties and its `makeNoise()` method.
In addition to the characteristics it inherits,
the `Bicycle` class defines a new stored property,
`hasBasket`, with a default value of `false`
(inferring a type of `Bool` for the property).
By default, any new `Bicycle` instance you create will not have a basket.
You can set the `hasBasket` property to `true` for a particular `Bicycle` instance
after that instance is created:
```swift
let bicycle = Bicycle()
bicycle.hasBasket = true
```
<!--
- test: `inheritance`
```swifttest
-> let bicycle = Bicycle()
-> bicycle.hasBasket = true
```
-->
You can also modify the inherited `currentSpeed` property of a `Bicycle` instance,
and query the instance's inherited `description` property:
```swift
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> bicycle.currentSpeed = 15.0
-> print("Bicycle: \(bicycle.description)")
</ Bicycle: traveling at 15.0 miles per hour
```
-->
Subclasses can themselves be subclassed.
The next example creates a subclass of `Bicycle` for a two-seater bicycle
known as a “tandem”:
```swift
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
```
<!--
- test: `inheritance`
```swifttest
-> class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
```
-->
`Tandem` inherits all of the properties and methods from `Bicycle`,
which in turn inherits all of the properties and methods from `Vehicle`.
The `Tandem` subclass also adds a new stored property called `currentNumberOfPassengers`,
with a default value of `0`.
If you create an instance of `Tandem`,
you can work with any of its new and inherited properties,
and query the read-only `description` property it inherits from `Vehicle`:
```swift
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> let tandem = Tandem()
-> tandem.hasBasket = true
-> tandem.currentNumberOfPassengers = 2
-> tandem.currentSpeed = 22.0
-> print("Tandem: \(tandem.description)")
</ Tandem: traveling at 22.0 miles per hour
```
-->
## Overriding
A subclass can provide its own custom implementation of
an instance method, type method, instance property, type property, or subscript
that it would otherwise inherit from a superclass.
This is known as *overriding*.
To override a characteristic that would otherwise be inherited,
you prefix your overriding definition with the `override` keyword.
Doing so clarifies that you intend to provide an override
and haven't provided a matching definition by mistake.
Overriding by accident can cause unexpected behavior,
and any overrides without the `override` keyword are
diagnosed as an error when your code is compiled.
The `override` keyword also prompts the Swift compiler
to check that your overriding class's superclass (or one of its parents)
has a declaration that matches the one you provided for the override.
This check ensures that your overriding definition is correct.
### Accessing Superclass Methods, Properties, and Subscripts
When you provide a method, property, or subscript override for a subclass,
it's sometimes useful to use the existing superclass implementation
as part of your override.
For example, you can refine the behavior of that existing implementation,
or store a modified value in an existing inherited variable.
Where this is appropriate,
you access the superclass version of a method, property, or subscript
by using the `super` prefix:
- An overridden method named `someMethod()` can call the superclass version of `someMethod()`
by calling `super.someMethod()` within the overriding method implementation.
- An overridden property called `someProperty` can access the superclass version of `someProperty`
as `super.someProperty` within the overriding getter or setter implementation.
- An overridden subscript for `someIndex` can access the superclass version of the same subscript
as `super[someIndex]` from within the overriding subscript implementation.
### Overriding Methods
You can override an inherited instance or type method
to provide a tailored or alternative implementation of the method within your subclass.
The following example defines a new subclass of `Vehicle` called `Train`,
which overrides the `makeNoise()` method that `Train` inherits from `Vehicle`:
```swift
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```
-->
If you create a new instance of `Train` and call its `makeNoise()` method,
you can see that the `Train` subclass version of the method is called:
```swift
let train = Train()
train.makeNoise()
// Prints "Choo Choo".
```
<!--
- test: `inheritance`
```swifttest
-> let train = Train()
-> train.makeNoise()
<- Choo Choo
```
-->
### Overriding Properties
You can override an inherited instance or type property
to provide your own custom getter and setter for that property,
or to add property observers to enable the overriding property
to observe when the underlying property value changes.
#### Overriding Property Getters and Setters
You can provide a custom getter (and setter, if appropriate)
to override *any* inherited property,
regardless of whether the inherited property is implemented as
a stored or computed property at source.
The stored or computed nature of an inherited property isn't known by a subclass ---
it only knows that the inherited property has a certain name and type.
You must always state both the name and the type of the property you are overriding,
to enable the compiler to check that your override matches
a superclass property with the same name and type.
You can present an inherited read-only property as a read-write property
by providing both a getter and a setter in your subclass property override.
You can't, however, present an inherited read-write property as a read-only property.
> Note: If you provide a setter as part of a property override,
> you must also provide a getter for that override.
> If you don't want to modify the inherited property's value within the overriding getter,
> you can simply pass through the inherited value
> by returning `super.someProperty` from the getter,
> where `someProperty` is the name of the property you are overriding.
The following example defines a new class called `Car`,
which is a subclass of `Vehicle`.
The `Car` class introduces a new stored property called `gear`,
with a default integer value of `1`.
The `Car` class also overrides the `description` property it inherits from `Vehicle`,
to provide a custom description that includes the current gear:
```swift
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
```
-->
The override of the `description` property starts by calling `super.description`,
which returns the `Vehicle` class's `description` property.
The `Car` class's version of `description` then adds some extra text onto
the end of this description to provide information about the current gear.
If you create an instance of the `Car` class
and set its `gear` and `currentSpeed` properties,
you can see that its `description` property returns
the tailored description defined within the `Car` class:
```swift
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
```
<!--
- test: `inheritance`
```swifttest
-> let car = Car()
-> car.currentSpeed = 25.0
-> car.gear = 3
-> print("Car: \(car.description)")
</ Car: traveling at 25.0 miles per hour in gear 3
```
-->
#### Overriding Property Observers
You can use property overriding to add property observers to an inherited property.
This enables you to be notified when the value of an inherited property changes,
regardless of how that property was originally implemented.
For more information on property observers, see <doc:Properties#Property-Observers>.
> Note: You can't add property observers to
> inherited constant stored properties or inherited read-only computed properties.
> The value of these properties can't be set,
> and so it isn't appropriate to provide a `willSet` or `didSet` implementation
> as part of an override.
>
> Note also that you can't provide both
> an overriding setter and an overriding property observer for the same property.
> If you want to observe changes to a property's value,
> and you are already providing a custom setter for that property,
> you can simply observe any value changes from within the custom setter.
The following example defines a new class called `AutomaticCar`,
which is a subclass of `Car`.
The `AutomaticCar` class represents a car with an automatic gearbox,
which automatically selects an appropriate gear to use based on the current speed:
```swift
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
```
-->
Whenever you set the `currentSpeed` property of an `AutomaticCar` instance,
the property's `didSet` observer sets the instance's `gear` property to
an appropriate choice of gear for the new speed.
Specifically, the property observer chooses a gear that's
the new `currentSpeed` value divided by `10`,
rounded down to the nearest integer, plus `1`.
A speed of `35.0` produces a gear of `4`:
```swift
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
```
<!--
- test: `inheritance`
```swifttest
-> let automatic = AutomaticCar()
-> automatic.currentSpeed = 35.0
-> print("AutomaticCar: \(automatic.description)")
</ AutomaticCar: traveling at 35.0 miles per hour in gear 4
```
-->
## Preventing Overrides
You can prevent a method, property, or subscript from being overridden
by marking it as *final*.
Do this by writing the `final` modifier before
the method, property, or subscript's introducer keyword
(such as `final var`, `final func`, `final class func`, and `final subscript`).
Any attempt to override a final method, property, or subscript in a subclass
is reported as a compile-time error.
Methods, properties, or subscripts that you add to a class in an extension
can also be marked as final within the extension's definition.
For more information, see <doc:Extensions>.
<!--
- test: `finalPreventsOverriding`
```swifttest
-> class C {
final var someVar = 0
final func someFunction() {
print("In someFunction")
}
}
-> class D : C {
override var someVar: Int {
get { return 1 }
set {}
}
override func someFunction() {
print("In overridden someFunction")
}
}
!$ error: property overrides a 'final' property
!! override var someVar: Int {
!! ^
!$ note: overridden declaration is here
!! final var someVar = 0
!! ^
!$ error: instance method overrides a 'final' instance method
!! override func someFunction() {
!! ^
!$ note: overridden declaration is here
!! final func someFunction() {
!! ^
```
-->
You can mark an entire class as final by writing the `final` modifier
before the `class` keyword in its class definition (`final class`).
Any attempt to subclass a final class is reported as a compile-time error.
<!--
- test: `finalClassPreventsOverriding`
```swifttest
-> final class C {
var someVar = 0
func someFunction() {
print("In someFunction")
}
}
-> class D : C {
override var someVar: Int {
get { return 1 }
set {}
}
override func someFunction() {
print("In overridden someFunction")
}
}
!$ error: property overrides a 'final' property
!! override var someVar: Int {
!! ^
!$ note: overridden declaration is here
!! var someVar = 0
!! ^
!$ error: instance method overrides a 'final' instance method
!! override func someFunction() {
!! ^
!$ note: overridden declaration is here
!! func someFunction() {
!! ^
!$ error: inheritance from a final class 'C'
!! class D : C {
!! ^
```
-->
<!--
TODO: I should probably provide an example here.
-->
<!--
TODO: provide more information about function signatures,
and what does / doesn't make them unique.
For example, the parameter names don't have to match
in order for a function to override a similar signature in its parent.
(This is true for both of the function declaration syntaxes.)
-->
<!--
TODO: Mention that you can return more-specific types, and take less-specific types,
when overriding methods that use optionals / unchecked optionals.
TODO: Overriding Type Methods
-->
<!--
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-->