Open In App

Swift – Inheritance

Last Updated : 04 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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.

  • The class that inherits from another class is called a subclass and the class that is inherited from is called a superclass.
  • For example, if we have a class called Animal and another class called Dog that inherits from Animal, then Dog is a subclass of Animal and Animal is a superclass of Dog.
  • In Swift, we use the colon “:” to indicate that a class inherits from another class.

Below is a Swift program to implement inheritance:

Swift




// 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:

  • In this code, we showcase the principles of inheritance in Swift. When a class inherits from a superclass, it gains access to the methods and properties of that superclass.
  • This means that the Dog subclass, in our example, inherits the species property and the speak method from its Animal superclass.
  • However, the power of inheritance extends beyond mere access. it enables us to extend and tailor the inherited functionalities. In the Dog subclass, we introduce a new property named breed and a method called wagTail.
  • This illustrates one of the primary advantages of inheritance – the ability to build on existing code while adding specialized behaviors.
  • By following this approach, we effectively prevent the need to duplicate code across multiple classes.
  • This not only streamlines our code but also enhances its modularity and maintainability.
  • It’s a prime example of how inheritance can help us create more efficient and adaptable software systems in Swift.

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.

  • For example, we may want to change how an animal makes noise or add more information to its description.
  • To do this, we can override the method or property in the subclass and provide a new implementation.
  • This is called method overriding or property overriding.
  • In Swift, we use the override keyword to indicate that we are overriding a method or a property in a subclass.

Below is Swift program to override methods and properties:

Swift




// 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:

  • The code defines a superclass Animal with properties and methods.
  • A subclass Dog inherits from Animal and customizes the description and makeNoise() properties and methods.
  • An instance of a Dog named Rex is created.
  • The overridden description property of Dog displays “I am a dog named Rex.”
  • The overridden makeNoise() method of Dog prints “I can bark.”
  • Another subclass Labrador is defined, as inheriting from “Dog.”
  • The description property of Labrador is overridden, including the original superclass description using super.
  • An instance of Labrador named Max is created.
  • The overridden description property of Labrador combines both class names, displaying “I am a labrador named Max, I am a dog named Max.”
  • The makeNoise() method inherited from Dog still prints “I can bark.”

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:

  • A car is a vehicle.
  • An apple is a fruit.
  • A cat is an animal.

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

Swift




// 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




// 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:

  • The code overrides the eat() method in the Dog subclass, which is originally defined in the Animal superclass.
  • When you call the eat() method using a Dog object, the version of the method in the Dog class is executed.
  • This behavior occurs because method overriding allows the subclass to provide its implementation of the method, replacing the one from the superclass.
  • It enables customization and specialization of method behavior in the subclass while maintaining a consistent interface with the superclass.

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




// 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:

  • The code overrides the eat() method in the Dog subclass, which originally belonged to the Animal superclass.
  • By using super.eat(), we explicitly call the eat() method of the Animal superclass within the Dog subclass.
  • When you call the eat() method using a Dog object, both the overridden eat() method in the Dog class and the superclass eat() method in the Animal class are executed.
  • This behavior occurs because the super keyword allows you to access and invoke the superclass’s implementation of a method.
  • It provides a way to extend or build upon the functionality of the superclass method while still using it.

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




// 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:

  • The code overrides the currentSpeed property in the Car subclass, originally defined in the Vehicle superclass.
  • Custom getter and setter are provided in the Car subclass to convert between miles per hour and kilometers per hour when accessing or setting the property.
  • The super keyword is used to access and modify the stored value of currentSpeed in the Vehicle superclass.
  • This allows for specialized handling of the property within the subclass while still interacting with and modifying the underlying value in the superclass.

Example 2:

Swift




// 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:

  • In this code, the description property of the Car subclass is overridden, originally defined in the Vehicle superclass.
  • A custom getter is provided for the description property within the Car subclass.
  • The custom getter appends “in a car” to the value returned by super.description, which is the description property of the Vehicle superclass.
  • This customization allows you to modify the behavior of the description property for the Car subclass while incorporating the description from the superclass.

Example 3:

Swift




// 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:

  • In this code, the currentSpeed property of the Car subclass is overridden, originally defined in the Vehicle superclass.
  • Custom willSet and didSet property observers are added to monitor and respond to changes in the property’s value.
  • In the willSet observer, you can access and work with the new value (newValue) before it’s set.
  • In the didSet observer, both the new value (newValue) and the previous value (oldValue) can be accessed for monitoring and responding to changes.
  • This customization allows you to track and react to changes in the currentSpeed property within the Car subclass.

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




// 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:

  • In this code, the currentSpeed property of the Vehicle class is marked as final.
  • The final keyword indicates that no subclass can override this property.
  • If an attempt is made to override this property in the Car class or any other subclass, it will result in a compilation error.
  • Marking a property or method as final prevents further modification in subclasses, providing a way to enforce the immutability or specific behavior of that element.

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.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads