Swift Inheritance
Inheritance lets one class acquire the properties and methods of another class. The class that shares its features is the parent (or superclass). The class that receives them is the child (or subclass). Think of it like a family tree — children inherit traits from parents but can also develop their own unique characteristics.
Basic Inheritance
class Animal {
var name: String
init(name: String) {
self.name = name
}
func speak() {
print("\(name) makes a sound.")
}
}
class Dog: Animal {
func fetch() {
print("\(name) fetches the ball!")
}
}
let dog = Dog(name: "Rex")
dog.speak() // Output: Rex makes a sound. (inherited)
dog.fetch() // Output: Rex fetches the ball! (own method)Diagram: Inheritance Tree
+----------+
| Animal | ← Superclass
| name |
| speak() |
+----------+
/ \
+-------+ +---------+
| Dog | | Cat | ← Subclasses
| fetch()| | purr() |
+-------+ +---------+
Dog and Cat inherit name and speak() from Animal.
Overriding Methods
A subclass can replace (override) a superclass method with its own version. Use the override keyword.
class Cat: Animal {
override func speak() {
print("\(name) says: Meow!")
}
}
class Dog: Animal {
override func speak() {
print("\(name) says: Woof!")
}
}
let cat = Cat(name: "Whiskers")
let dog = Dog(name: "Rex")
cat.speak() // Output: Whiskers says: Meow!
dog.speak() // Output: Rex says: Woof!Calling the Superclass with super
Inside an overriding method, call the superclass version using super.
class Animal {
func speak() {
print("Animal speaks.")
}
}
class Dog: Animal {
override func speak() {
super.speak() // Calls Animal's speak()
print("Dog barks too!")
}
}
Dog().speak()
// Output:
// Animal speaks.
// Dog barks too!Overriding Initializers
class Vehicle {
var speed: Int
init(speed: Int) {
self.speed = speed
}
}
class ElectricCar: Vehicle {
var batteryLevel: Int
init(speed: Int, batteryLevel: Int) {
self.batteryLevel = batteryLevel
super.init(speed: speed) // Must call superclass init
}
func status() {
print("Speed: \(speed) km/h | Battery: \(batteryLevel)%")
}
}
let tesla = ElectricCar(speed: 150, batteryLevel: 80)
tesla.status()
// Output: Speed: 150 km/h | Battery: 80%Overriding Properties
You can override a stored property from the superclass as a computed property in the subclass.
class Shape {
var description: String {
return "A shape"
}
}
class Square: Shape {
var side: Double
init(side: Double) {
self.side = side
}
override var description: String {
return "A square with side \(side)"
}
}
let sq = Square(side: 4.0)
print(sq.description) // Output: A square with side 4.0Preventing Override with final
Mark a method or class with final to prevent subclasses from overriding it.
class PaymentProcessor {
final func processPayment(amount: Double) {
print("Processing \(amount)...")
}
}
// class FakeProcessor: PaymentProcessor {
// override func processPayment(amount: Double) { }
// ↑ ERROR: Cannot override 'final' method
// }
final class SecureVault {
// Nothing can subclass SecureVault
}Polymorphism
Polymorphism means one type can take many forms. A variable of type Animal can actually hold a Dog or a Cat. Swift calls the correct overridden method at runtime.
let animals: [Animal] = [Dog(name: "Rex"), Cat(name: "Whiskers"), Dog(name: "Buddy")]
for animal in animals {
animal.speak()
}
// Output:
// Rex says: Woof!
// Whiskers says: Meow!
// Buddy says: Woof!Type Casting
Checking Type with is
let animal: Animal = Dog(name: "Rex")
if animal is Dog {
print("It's a dog!")
}
// Output: It's a dog!Downcasting with as? and as!
if let dog = animal as? Dog {
dog.fetch() // Output: Rex fetches the ball!
}Use as? (safe) to attempt a downcast and receive nil on failure. Use as! only when you are certain the type is correct.
Diagram: Polymorphism at Runtime
[Animal array] | +→ Dog → dog.speak() → "Rex says: Woof!" +→ Cat → cat.speak() → "Whiskers says: Meow!" +→ Dog → dog.speak() → "Buddy says: Woof!" Same method call, different behavior — decided at runtime.
Summary
Inheritance lets subclasses reuse and extend superclass functionality. Use override to replace methods, super to call the parent version, and final to lock down critical logic. Polymorphism lets collections of parent-typed variables run different code at runtime based on the actual subclass stored inside.
