Open In App

Swift – Inheritance

Inheritance is one of the fundamental concepts of object-oriented programming (OOP) languages. It allows us to create a new class from an existing class and reuse the existing code and functionality. In this article, we will learn about inheritance in Swift and how to use it to create subclasses, override methods and properties, and implement polymorphism.

What is Inheritance?

Inheritance is a relationship between two classes, where one class inherits the methods, properties, and other characteristics from another class.



Below is a Swift program to implement inheritance:




// Swift program to implement
// Inheritance
// Define a superclass
class Animal
  // Property in the superclass
  var species: String = ""
 
  // Method in the superclass
  func speak()
  {
    print("An animal of species \(species) is making a sound.")
  }
}
 
// Define a subclass that inherits from Animal
class Dog: Animal
{
  // Additional properties and methods in the subclass
  var breed: String = ""
   
  func wagTail()
  {
    print("The dog of breed \(breed) is wagging its tail.")
  }
}
 
// Create an instance of Dog
let myDog = Dog()
 
// Set values for properties
myDog.species = "Canine"
myDog.breed = "Golden Retriever"
 
// Access methods and properties
// An animal of species Canine is making a sound.
myDog.speak() 
 
// The dog of breed Golden Retriever is wagging its tail.
myDog.wagTail()

Output:



An animal of species Canine is making a sound.
The dog of breed Golden Retriever is wagging its tail.

Explanation:

How to Override Methods and Properties?

Sometimes, we may want to modify or extend the behavior of a method or a property that is inherited from a superclass.

Below is Swift program to override methods and properties:




// Swift program to override methods
// and properties
// Define a superclass
class Animal
{
  // Properties and methods of the superclass
  var name: String = ""
 
  var description: String
  {
    return "I am an animal named \(name)"
  }
   
  func makeNoise()
  {
    print("I can make noise")
  }
}
 
// Define a subclass that inherits from Animal
class Dog: Animal
{
  // Override the computed property
  override var description: String
  {
    return "I am a dog named \(name)"
  }
   
  // Override the method
  override func makeNoise()
  {
    print("I can bark")
  }
}
 
// Create an instance of Dog
let dog = Dog()
 
// Assign a name to the dog
dog.name = "Rex"
 
// Access the overridden computed property
// I am a dog named Rex
print(dog.description)
 
// Call the overridden method
// I can bark
dog.makeNoise()
 
// Define a subclass that inherits from Dog
class Labrador: Dog
{
  // Override the computed property again
  override var description: String
  {
    return "I am a labrador named \(name), \(super.description)"
  }
}
 
// Create an instance of Labrador
let lab = Labrador()
 
// Assign a name to the labrador
lab.name = "Max"
 
// Access the overridden computed property
// I am a labrador named Max, I am a dog named Max
print(lab.description)
 
// Call the inherited method
// I can bark
lab.makeNoise()

Output:

I am a dog named Rex
I can bark
I am a labrador named Max, I am a dog named Max
I can bark

Explanation:

Types of Inheritance

Swift supports different types of inheritance, depending on the relationship between the classes involved. Some of the common types of inheritance are:

1. Single inheritance

A subclass inherits from only one superclass. This is the most common type of inheritance in Swift. For example:

class Vehicle 
{
// properties and methods of Vehicle
}
class Car: Vehicle
{
// properties and methods of Car
}

Here, the Car class inherits from the Vehicle class, which means that a Car instance can access the properties and methods of the Vehicle class, as well as its properties and methods.

2. Multilevel inheritance

A subclass inherits from another subclass, which in turn inherits from a superclass. This creates a chain of inheritance from the top-level superclass to the bottom-level subclass. For example:

class Vehicle 
{
// properties and methods of Vehicle
}
class Car: Vehicle
{
// properties and methods of Car
}
class ElectricCar: Car
{
// properties and methods of ElectricCar
}

Here, the ElectricCar class inherits from the Car class, which in turn inherits from the Vehicle class. This means that an ElectricCar instance can access the properties and methods of all three classes in the hierarchy.

3. Multiple inheritance

A subclass inherits from more than one superclass. This allows a subclass to combine the features and behaviors of multiple superclasses. However, Swift does not support multiple inheritance for classes directly. Instead, Swift uses protocols to achieve a similar effect. A protocol defines a set of requirements that a conforming type must implement, such as properties, methods or subscripts. A class can conform to multiple protocols by listing them after the colon: in its declaration. For example:

protocol Flyable 
{
// requirements for flying
}
protocol Swimable
{
// requirements for swimming
}
class Duck: Animal, Flyable, Swimable
{
// properties and methods of Duck
}

Here, the Duck class inherits from the Animal class and also conforms to the Flyable and Swimable protocols. This means that a Duck instance can access the properties and methods of the Animal class, as well as the requirements of the two protocols.

Is-a Relationship

In Swift, inheritance is an is-a relationship. That is, we use inheritance only if there exists an is-a relationship between two classes. An is-a relationship means that a subclass is a specific type or kind of its superclass. For example:

Here, we can use inheritance to model these relationships by creating subclasses for each specific type or kind.




// Swift program to implement
// is-a relationship
// Define a base class for vehicles
class Vehicle
{
  var brand: String
  var year: Int
     
  // Initialize the vehicle with a brand and year
  init(brand: String, year: Int)
  {
    self.brand = brand
    self.year = year
  }
    
  // Method to start the vehicle's engine
  func startEngine()
  {
    print("The \(brand) vehicle's engine is starting.")
  }
}
 
// Create a subclass for cars
class Car: Vehicle
{
  var model: String
   
  // Initialize the car with a brand, year, and model
  init(brand: String, year: Int, model: String)
  {
    self.model = model
    super.init(brand: brand, year: year)
  }
   
  // Method to drive the car
  func drive()
  {
    print("The \(year) \(brand) \(model) is driving.")
  }
}
 
// Define a base class for fruits
class Fruit
{
  var name: String
   
  // Initialize the fruit with a name
  init(name: String)
  {
    self.name = name
  }
     
  // Method to describe the fruit
  func describe()
  {
    print("This is a \(name).")
  }
}
 
// Create a subclass for apples
class Apple: Fruit
{
  var variety: String
    
  // Initialize the apple with a name and variety
  init(name: String, variety: String)
  {
    self.variety = variety
    super.init(name: name)
  }
     
  // Method to describe the taste of the apple
  func taste()
  {
    print("This \(name) is a \(variety) apple, and it tastes delicious.")
  }
}
 
// Define a base class for animals
class Animal
{
  var species: String
     
  // Initialize the animal with a species
  init(species: String)
  {
    self.species = species
  }
     
  // Method to make a sound
  func makeSound()
  {
    print("The \(species) is making a sound.")
  }
}
 
// Create a subclass for cats
class Cat: Animal
{
  var name: String
   
  // Initialize the cat with a name
  init(name: String)
  {
    self.name = name
    super.init(species: "Cat")
  }
    
  // Method for a cat to meow
  func meow()
  {
    print("\(name) the cat says 'Meow!'")
  }
}
 
// Creating and using objects
let myCar = Car(brand: "Toyota",
                year: 2023, model: "Camry")
myCar.startEngine()
myCar.drive()
 
let myApple = Apple(name: "Apple",
                    variety: "Honeycrisp")
myApple.describe()
myApple.taste()
 
let myCat = Cat(name: "Whiskers")
myCat.makeSound()
myCat.meow()

Output:

The Toyota vehicle's engine is starting.
The 2023 Toyota Camry is driving.
This is a Apple.
This Apple is a Honeycrisp apple, and it tastes delicious.
The Cat is making a sound.
Whiskers the cat says 'Meow!'

Using inheritance to express an is-a relationship helps us to reuse code, avoid duplication and maintain consistency across related classes.

Method Overriding

A subclass can provide its 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, we prefix our overriding definition with the override keyword.

Below is the Swift program to implement method overriding:




// Swift program to implement
// method overriding
 
// Define a base class for animals
class Animal
{
  // Method in the superclass
  func eat()
  {
    print("I can eat")
  }
}
 
// Create a subclass for dogs,
// which inherits from Animal
class Dog: Animal
{
  // Override the eat() method in the subclass
  override func eat()
  {
    print("I eat dog food")
  }
}
 
// Creating and using objects
let genericAnimal = Animal()
let myDog = Dog()
 
genericAnimal.eat()
myDog.eat()

Output

I can eat
I eat dog food

Explanation:

Overriding allows us to customize and modify the behavior of inherited methods, properties or subscripts according to the needs of the subclass.

super Keyword

In Swift, we can access methods, properties or subscripts of the superclass from a subclass using the super keyword. The super keyword refers to the superclass instance within the current subclass instance. This allows us to invoke the superclass’s implementation of a method or access its properties.

Below is the Swift program to implement super keyword:




// Swift program to implement
// super keyword
 
// Define a base class for animals
class Animal
{
  // Method in the superclass
  func eat()
  {
    print("I can eat")
  }
}
 
// Create a subclass for dogs,
// which inherits from Animal
class Dog: Animal
{
  // Override the eat() method in the subclass
  override func eat()
  {
    // Call the eat() method of the superclass
    super.eat()
         
    // Add additional behavior
    print("I eat dog food")
  }
}
 
// Create an object of the subclass
// (a Labrador dog)
var labrador = Dog()
 
// Call the eat() method on the
// Labrador object
labrador.eat()

Output

I can eat
I eat dog food

Explanation:

Using super enables us to access and utilize inherited characteristics of the superclass from within the subclass, allowing for the customization of behavior while preserving and extending the functionality of the parent class.

Property Overriding

A subclass can also override inherited properties of a superclass by providing its custom getter and setter for the properties or adding property observers to them. Property observers are code blocks that are executed when a property’s value is set or changed. There are two types of property observers: willSet and didSet. The willSet observer is called before the value of a property is stored and the didSet observer is called after the value of a property is stored.

To override a property, we use the same syntax as for overriding a method, but we also specify what aspect of the property we want to override: its getter, its setter or its observers.

Example 1: Below is the Swift program to implement property overriding:




// Swift program to implement
// property overriding
 
// Define a base class for vehicles
class Vehicle
{
  // Property in the superclass to represent
  // the current speed in miles per hour (mph)
  var currentSpeed = 0.0
}
 
// Create a subclass for cars, which
// inherits from Vehicle
class Car: Vehicle
{
  // Override the property with a custom getter
  // and setter to convert speed to kilometers
  // per hour (kph)
  override var currentSpeed: Double
  {
    get
    {
      // Custom getter to convert mph to kph
      // Convert mph to kph
      return super.currentSpeed * 1.6
    }
    set
    {
      // Custom setter to convert kph back to mph
      // Convert kph to mph
      super.currentSpeed = newValue / 1.6
    }
  }
}
 
// Create an instance of the Car class
let myCar = Car()
 
// Set the current speed in miles per hour (mph)
// Set the speed to 60 mph
myCar.currentSpeed = 60.0
 
// Retrieve and print the current speed in
// kilometers per hour (kph)
let speedInKPH = myCar.currentSpeed
print("Current speed: \(speedInKPH) kph")

Output

Current speed: 60.0 kph

Explanation:

Example 2:




// Swift program to implement
// Property overriding
 
// Define a base class for vehicles
class Vehicle
{
  // Computed property in the superclass
  // to describe the vehicle's speed
  var description: String
  {
    return "traveling at \(currentSpeed) miles per hour"
  }
     
  // Property to represent the current speed
  // in miles per hour (mph)
  var currentSpeed = 0.0
}
 
// Create a subclass for cars, which inherits
// from Vehicle
class Car: Vehicle
{
  // Override the description property with a custom
  // getter to include "in a car"
  override var description: String
  {
    return super.description + " in a car"
  }
}
 
// Create an instance of the Car class
let myCar = Car()
 
// Set the speed to 60 mph
myCar.currentSpeed = 60.0
 
// Access and print the description property
let carDescription = myCar.description
print(carDescription)

Output

traveling at 60.0 miles per hour in a car

Explanation:

Example 3:




// Swift program to implement
// Property overriding
 
// Define a base class for vehicles
class Vehicle
{
  // Property in the superclass with a
  // didSet observer
  var currentSpeed = 0.0
  {
    didSet
    {
      print("The speed changed to \(currentSpeed)")
    }
  }
}
 
// Create a subclass for cars, which inherits
// from Vehicle
class Car: Vehicle
{
  // Override the property with custom willSet
  // and didSet observers
  override var currentSpeed: Double
  {
    willSet
    {
      print("The speed will change to \(newValue)")
    }
    didSet
    {
      print("The speed changed by \(currentSpeed - oldValue)")
    }
  }
}
 
// Create an instance of the Car class
let myCar = Car()
 
// Set the current speed, triggering willSet
// and didSet observers
myCar.currentSpeed = 60.0
 
// Set the speed again to observe changes
myCar.currentSpeed = 80.0

Output

The speed will change to 60.0
The speed changed to 60.0
The speed changed by 60.0
The speed will change to 80.0
The speed changed to 80.0
The speed changed by 20.0

Explanation:

Overriding properties allows us to customize and modify how inherited properties are accessed or observed according to the needs of the subclass.

Final Property

Swift allows us to prevent a subclass from overriding a method, property, or subscript of a superclass by using the final modifier. We write the final keyword before the declaration of the characteristic that we want to protect from overriding (such as final var, final func, final class func and final subscript). If a subclass tries to override a final characteristic, it will cause a compile-time error.

Below is the Swift program to implement Final property:




// Swift program to implement
// Final
 
// Define a base class for vehicles
class Vehicle
{
  // Mark the property as final to prevent
  // overriding in subclasses
  final var currentSpeed = 0.0
}
 
// Create a subclass for cars, which
// inherits from Vehicle
class Car: Vehicle
{
  // Attempt to override the final property
  // (this will cause an error)
  // Uncomment the following lines to see the error:
   
  /*
  override var currentSpeed: Double
  {
     // This will result in a compilation error
  }
  */
}
 
// Attempting to create instances and override
// the final property will result in an error
 
// Uncomment these lines to observe the error:
/*
let myVehicle = Vehicle()
myVehicle.currentSpeed = 60.0 // Error: Cannot assign
to property: 'currentSpeed' is a 'final' property
 
let myCar = Car()
myCar.currentSpeed = 80.0 // Error: Cannot assign to
property: 'currentSpeed' is a 'final' property
*/

Explanation:

Using the final modifier helps us ensure that a superclass characteristic’s behavior and functionality are consistent and reliable across all subclasses.

Conclusion

In this article, we have learned about inheritance in Swift and how to use it to create subclasses, override methods and properties, and implement polymorphism. Inheritance is a powerful feature of object-oriented programming that allows us to reuse and extend existing code and functionality. It also helps us to model real-world objects and relationships in our programs. We have also seen some of the types of inheritance and the is-a relationship that exists between classes. We have also learned how to use the super keyword to access the superclass characteristics from the subclass and how to use the final keyword to prevent overriding. We hope this article has helped you to understand the concept of inheritance in Swift better.


Article Tags :