Skip to Content
Course content

18: Inheritance and Polymorphism

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Inheritance and polymorphism sound like academic buzzwords, but in the real world, they're just tools to stop you from writing the same code ten times. I like to think of inheritance as "is-a" relationship: a Credit Card payment is a Payment Method. Polymorphism is the magic that lets us treat a whole group of different objects as if they were the same thing, while still letting each one keep its own unique behavior.

Defining the shared blueprint

Let's build a simple payment processing system. If we have five different ways to pay, we don't want to write five different functions to handle them. Instead, I'll start by creating a base class that holds everything every payment method needs—like the amount.

class PaymentMethod {
    var amount: Double
    
    init(amount: Double) {
        self.amount = amount
    }
    
    func processPayment() {
        print("Processing a generic payment of $\(amount)")
    }
}

This is our foundation. Any class that inherits from PaymentMethod will automatically have an amount and a processPayment method. It's a way of saying, "I don't care how you do it, but if you want to be a PaymentMethod, you must be able to process a payment."

Specializing the payment types

Now, a Credit Card payment needs a card number, and a PayPal payment needs an email address. I'll create subclasses for these. I'll use the override keyword to change how processPayment works for each one.

class CreditCardPayment: PaymentMethod {
    var cardNumber: String
    
    init(amount: Double, cardNumber: String) {
        self.cardNumber = cardNumber
        super.init(amount: amount)
    }
    
    override func processPayment() {
        print("Charging $\(amount) to card \(cardNumber)... Success!")
    }
}

class PayPalPayment: PaymentMethod {
    var email: String
    
    init(amount: Double, email: String) {
        self.email = email
        super.init(amount: amount)
    }
    
    override func processPayment() {
        print("Redirecting to PayPal for \(email) to pay $\(amount)... Success!")
    }
}

Notice the super.init call. In Swift, you have to initialize your own properties before you can tell the parent class to initialize its properties. It's a strict rule, but it prevents the app from crashing because of uninitialized data.

The "Wait, why isn't this compiling?" moment

Here is where I usually trip up when I'm moving fast. I decided to put all my payments into a single array so I could process them in one loop. I tried to do something like this:

let myPayments: [PaymentMethod] = [
    CreditCardPayment(amount: 50.0, cardNumber: "1234-5678"),
    PayPalPayment(amount: 25.0, email: "dev@example.com")
]

for payment in myPayments {
    // I tried to access the card number here...
    print(payment.cardNumber) 
}

The compiler immediately yelled at me. Why? Because the myPayments array is typed as [PaymentMethod]. The compiler only knows that every item in that list is a PaymentMethod, and PaymentMethod doesn't have a cardNumber property. Only the CreditCardPayment subclass has that.

The mistake was trying to treat a generic parent as a specific child. The fix is to rely on the shared interface—the method they both have in common.

Treating everything as a PaymentMethod

This is where polymorphism actually shines. Instead of trying to access specific properties, I just call the method that exists on the base class. Swift is smart enough to know which version of the method to run based on the actual type of the object at runtime.

let myPayments: [PaymentMethod] = [
    CreditCardPayment(amount: 50.0, cardNumber: "1234-5678"),
    PayPalPayment(amount: 25.0, email: "dev@example.com"),
    PaymentMethod(amount: 10.0) // A generic payment
]

for payment in myPayments {
    // This is polymorphism! 
    // One call, three different behaviors.
    payment.processPayment()
}

If you run this, you'll see the card-specific message for the first item, the PayPal message for the second, and the generic message for the third. I didn't have to write a single if statement or switch case to check the type. The objects just "know" how to behave.




📋 Practical Task

Building a Multimedia Notification System

You need to build a system that sends notifications to users via different channels. Some users want Emails, and some want SMS messages. Your goal is to use inheritance and polymorphism to handle these different channels without writing separate loops for each.

  • Create a base class called Notification with a property message: String and a method send() that prints "Sending generic notification: [message]".
  • Create a subclass EmailNotification that adds an emailAddress: String property and overrides send() to print "Sending Email to [emailAddress]: [message]".
  • Create a subclass SMSNotification that adds a phoneNumber: String property and overrides send() to print "Sending SMS to [phoneNumber]: [message]".
  • Create an array of type [Notification] containing at least one of each type.
  • Loop through the array and call send() on each object to demonstrate polymorphism.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.