Inheritance and super
Prerequisites: Class methods
Inheritance lets one class build on another. The new class (the subclass) gets all the methods of the existing one (the superclass) and can add or change behavior.
Real-world analogy: every car is a vehicle. A Car class can
inherit everything general from Vehicle (wheels, a move method) and
add what's specific to cars.
Syntax
class Subclass < Superclass
end
Example
class Animal
attr_reader :name
def initialize(name)
@name = name
end
def speak
"..."
end
def introduce
"I'm #{name} and I say #{speak}"
end
end
class Cat < Animal
def speak
"meow"
end
end
class Dog < Animal
def initialize(name, tricks)
super(name)
@tricks = tricks
end
def speak
"woof"
end
def introduce
super + " and I know #{@tricks.length} tricks"
end
end
puts Cat.new("Tom").introduce
puts Dog.new("Rex", ["sit", "roll"]).introduce
p Dog.superclass
p Dog.new("Rex", []).is_a?(Animal)
Expected output
I'm Tom and I say meow
I'm Rex and I say woof and I know 2 tricks
Animal
true
Line by line:
Cat < AnimalmakesCata subclass. It inheritsinitialize,name, andintroduce, and overridesspeak.introduceinAnimalcallsspeak— and gets the subclass's version. That's the key idea of inheritance.supercalls the superclass's version of the current method.super(name)passes justnameup toAnimal#initialize.- In
Dog#introduce,superreturns the parent's string, and the subclass adds to it.
super with no parentheses passes along all the arguments the
current method received; super() passes none. That difference
matters when the parent's initialize takes fewer arguments.
When to use inheritance
Use it for a true "is a kind of" relationship. For sharing a few abilities between unrelated classes, Ruby's modules (a later lesson) are usually a better fit. Deep inheritance chains quickly become hard to follow.
Common mistakes
- Overriding
initializein a subclass and forgetting to callsuper, so the parent's instance variables are never set. - Writing
superwhen you meantsuper(), and gettingwrong number of arguments.
Practice
Create a Shape class with an area method returning 0 and a
describe method returning "Area: #{area}". Add Square (side
length) and Circle (radius) subclasses that override area. Print
describe for a square of side 3 and a circle of radius 1 (rounded to
two decimals).
Show solution
class Shape
def area = 0
def describe = "Area: #{area}"
end
class Square < Shape
def initialize(side)
@side = side
end
def area = @side * @side
end
class Circle < Shape
def initialize(radius)
@radius = radius
end
def area = (Math::PI * @radius**2).round(2)
end
puts Square.new(3).describe
puts Circle.new(1).describe
Area: 9
Area: 3.14