Swift tutorial on inheritance

  • 2020-05-12 06:16:47
  • OfStack

One class can inherit methods, properties, or other properties from another class. When a class inherits from another class, the inherited class is called a subclass, and the inherited class is called a superclass. Inheritance is one of the essential characteristics that distinguish classes from other types in Swift.

The classes in Swift can call the methods of the parent class, use the properties and subscripts of the parent class, and redefine and modify some of their properties as needed using override methods or properties. Swift can help you check that the overridden method is consistent with the parent method definition.

A class can also add an observer to a property it inherits, which allows it to be notified when a property changes. An attribute observer can be added to any attribute, whether it was previously stored or evaluated.

1. Define a base class

Any class that does not inherit from another class is called a base class

Note: Swift's class does not inherit from a global base class. When you write code, any class that does not inherit from its parent in its class definition is a base class.

The following example defines a base class called Vehicle. The base class contains two properties numberOfWheels and maxPassengers common to all vehicles. These two properties are used by a method called description to characterize the vehicle by returning an String description:


class Vehicle {
    var numberOfWheels: Int
    var maxPassengers: Int
    func description() -> String {
        return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
    }
    init() {
        numberOfWheels = 0
        maxPassengers = 1
    }
}

The vehicle class Vehicle also defines a constructor to set its properties. Constructors are more explained in Initialization1, but to show how subclasses can modify inherited properties, here's a brief explanation of what constructors are.

The constructor allows you to create an instance of a type. Although constructors are not methods, they use a very similar syntax when coding. The constructor creates a new instance by ensuring that all the instance properties are valid.

The simplest form of the constructor is a function of a similar method using the init keyword and without any arguments:

 
init() {
    // perform some initialization here
}

Use the constructor syntax TypeName and two empty parentheses to create an instance of Vehicle:

 
let someVehicle = Vehicle()

The constructor for Vehicle sets some initial values for the property (numberOfWheels = 0 and maxPassengers = 1).

The Vehicle class defines a generic vehicle property, which by itself doesn't make much sense, so you need to define some of its properties or methods to make sense of it.

2. Subclass

Subclassing is the process of generating a new class from an existing class. The subclass inherits some of the modifiable features of the parent class. You can also add some new features to subclasses.

To indicate that a class is inherited from a parent class, write the name of the parent class after the child class and separate it with a colon:

 
class SomeClass: SomeSuperclass {
    // class definition goes here
}

The following example defines a particular type of vehicle called Bicycle. This new class is based on the existing class Vehicle. This is done by adding a colon and the parent Vehicle name after the class name Bicycle.

It can be understood as:

Define a new class called Bicycle, which inherits Vehicle's properties:

 
class Bicycle: Vehicle {
    init() {
        super.init()
        numberOfWheels = 2
    }
}

Bicycle is a subclass of Vehicle, and Vehicle is the parent of Bicycle. The Bicycle class inherits all of Vehicle's characteristics, such as maxPassengers and numberOfWheels properties. You can also add attributes to the Bicycle class.

The Bicycle class also defines a constructor in which the constructor super.init () of the parent class is called, ensuring that the parent class is initialized before Bicycle modifies them.

Note: unlike Objective-C, the constructor in Swift does not inherit by default. More information can be found in section 1 of Initializer Inheritance and Overriding.

The maxPassengers property is initialized when inherited from the parent class and is correct for Bicycle, so no further changes are required. And then numberOfWheels is not right, so it's replaced by 2.

Not only are attributes inherited from Vehicle, Bicycle also inherits methods from its parent class. If you create an instance and then call the inherited description method, you can get a description of the vehicle and see that its properties have been modified:

 
let bicycle = Bicycle()
println("Bicycle: \(bicycle.description())")
// Bicycle: 2 wheels; up to 1 passengers

A subclass itself can be inherited again as a parent:

 
class Tandem: Bicycle {
    init() {
        super.init()
        maxPassengers = 2
    }
}

The above example creates a subclass of Bicycle, called tandem, which allows two people to ride a bike. So Tandem didn't modify the numberOfWheels property, just updated the maxPassengers property.

Note: subclasses can only modify properties of variables during construction, not properties of constants.

Create an instance of Tandem and call the description method to check if the property has been modified correctly:

 
let tandem = Tandem()
println("Tandem: \(tandem.description())")
// Tandem: 2 wheels; up to 2 passengers

Notice that the description method is also inherited by Tandem.

3. Rewrite method

Subclasses can provide personalized implementations of instance methods, class methods, instance properties, or subscripts inherited from a parent class. This feature is called rewriting.

Overriding an inherited method requires that the override keyword be annotated before the method definition. This ensures that the method you are trying to modify is actually inherited and that there are no override errors. A bad rewrite can cause unpredictable errors, so if you don't mark the override keyword, you'll get an error in your code compilation.

The override keyword also enables the Swift compiler to check if the parent class of the class has a matching method to ensure that your override is available and correct.

Access the parent class methods, properties, and subscripts

When overriding a subclass that inherits a method, property, or subscript from a parent class, you need to use part 1 of the parent class's existing implementation. For example, you can redefine a known implementation or store a modified value in an inherited variable.

When appropriate, you can access the parent class's methods, properties, or subscripts by using the super prefix:

The override method called someMethod calls the someMethod method of the parent class through super.someMethod () at implementation time.

The override property called someProperty can call the parent class someProperty via super.someProperty when the override implements getter or setter.

The override subscript, called someIndex, accesses the subscript of the parent class through super[someIndex] when implementing the subscript.

Copying method

You can implement custom instance methods or class methods that inherit from the parent class in your subclass.

The following example demonstrates a subclass of Vehicle called Car, overriding the description method inherited from Vehicle.

 
class Car: Vehicle {
    var speed: Double = 0.0
    init() {
        super.init()
        maxPassengers = 5
        numberOfWheels = 4
    }
    override func description() -> String {
        return super.description() + "; "
            + "traveling at \(speed) mph"
    }
}

A new storage property of type Double, speed, is defined in Car. The default value of this property is 0.0, which means 0 miles per hour. Car also has a custom constructor that sets the maximum number of passengers to 5 and the number of wheels to 4.

Car overrides the inherited description method and notes the override keyword before the method name description.

Instead of a new description implementation in description, super.description USES some of the description statements provided by Vehicle and then adds some attributes of its own, such as the current speed.

If you create an instance of Car and then call the description method, you will find that the description statement looks like this:


let car = Car()
println("Car: \(car.description())")
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph

Copy attributes

You can also provide instance properties inherited from a parent class or personalized getter and setter methods for class properties, or you can add property observers to override properties that observe changes in inherited properties.

Override the Getters and Setters properties

Whether the property inherited in the source class is a stored property or a calculated property, you can override the inherited property with a custom getter or setter method. Subclass 1 generally does not know whether the inherited property was originally a stored property or a calculated property, but it does know that the property has a specific name and type. When overwriting, specify the type and name of the property so that the compiler can check if your overwriting matches the property of the parent class.

You can inherit a read-only property as read-write through the tienese getter and setter, but not vice versa.

Note: if you provide an setter method for an override property, you also need to provide an getter method. If you don't want to change the value of an inherited property in getter, you can just use super.someProperty in getter, as in the SpeedLimitedCar example below.

The following example defines a new class, SpeedLimitedCar, which is a subclass of Car. This class represents a vehicle displayed under 40 yards 1. By overriding the inherited speed property:


class SpeedLimitedCar: Car {
    override var speed: Double  {
    get {
        return super.speed
    }
    set {
        super.speed = min(newValue, 40.0)
    }
    }
}

Whenever you set the speed property, setter will check whether the new value is greater than 40, and the smaller of the two will be set to SpeedLimitedCar.

If you try to set speed above 40, the output of description will still be 40:


let limitedCar = SpeedLimitedCar()
limitedCar.speed = 60.0
println("SpeedLimitedCar: \(limitedCar.description())")
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph

Override attribute observer

You can use property overrides to add observers to inherited properties. This allows you to be reminded when an inherited property changes, regardless of how it was previously implemented. More information can be found in the Property Observers chapter.

Note: you cannot add an observer to an inherited constant storage property or to a read-only computed property. These property values cannot be modified, so it is not appropriate to add willSet or didSet methods when overriding the implementation.

Note: you cannot define both the overridden setter and the overridden property observer. If you want to observe a change in the property value and give a custom setter for that property, you can simply obtain the change in the property value directly in setter.

The following code demonstrates a new class, AutomaticCar, which is also a subclass of Car. This class indicates that a car with an automatic gearbox can automatically select the gear position according to the current speed and output the current gear position in description:


class AutomaticCar: Car {
    var gear = 1
    override var speed: Double {
    didSet {
        gear = Int(speed / 10.0) + 1
    }
    }
    override func description() -> String {
        return super.description() + " in gear \(gear)"
    }
}

That way, every time you set the value of speed, the didSet method is called to see if the tap position needs to change. gear is calculated by dividing speed by 10 plus 1, so when the speed is 35, gear gear is 4:


let automatic = AutomaticCar()
automatic.speed = 35.0
println("AutomaticCar: \(automatic.description())")
// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4

4. Do not rewrite

You can disable overwriting a class's methods, properties, or subscripts by marking the final keyword. Just mark the @final attribute in front of the defined keywords.

Any attempt to override a parent's final method, property, or subscript in a subclass will result in an error at compile time. The same method, property, or subscript added to the class in the extension can also be marked final.

You can also tag 1 with @final before the class keyword class. The entire class is final(@final class). Any subclass that attempts to inherit from this parent class will make a compilation error.


Related articles: