Inheritance
Inheritance is an important piece of OOP patterns. In simple terms, a class inheriting from another class means it'll get the parent classes methods, instance variables and it'll be able to add/overwrite existing ones.
Here's a basic example of inheritance:
class Animal
property name : String
property age : Int32
def initialize(@name, @age)
end
end
class Dog < Animal
property domesticated = true
property dexterity = 10
end
tex = Animal.new "Tex", 12
rex = Dog.new "Rex", 3
rex.domesticated = true
rex.dexterity = 8
# You can see below that the child class satisfies
# both the Parent and Child when is_a method is raised:
p tex.is_a? Animal # => true
p tex.is_a? Dog # => false
p rex.is_a? Animal # => true
p rex.is_a? Dog # => true
Inheritance Continued: Overriding
Overriding works very much the same way as it works with other OOP languages.
Basic example:
class Person
property name : String
def initialize(@name)
end
end
class Employee < Person
property salary = 0
end
tt = Employee.new "TT"
Reopening A Class
You might want to reopen a class for different reasons, Crystal allows this and the additions you make with the reopened class will be combined into a single class.
Have a look at the following example:
class Employee < Person
def yearly_salary
12 * @salary
end
end
class SalesEmployee < Employee
property bonus = 0
def yearly_salary
12 * @salary + @bonus
end
end
john = Person.new "John"
jane = Employee.new "Jane"
rich = SalesEmployee.new "Rich"
jane.salary = 2000
p jane.yearly_salary # => 24000
rich.salary = 2000
rich.bonus = 1000
p rich.yearly_salary # => 25000
This example can be further enhanced by the usage of the
super method where by calling super we get to use
the super class functionality:
class HrEmployee < Employee
property bonus = 0
def yearly_salary
super + @bonus
end
end
herbert = HrEmployee.new "Herbert"
herbert.salary = 2000
herbert.bonus = 2000
p herbert.yearly_salary # => 26000
Abstract Classes
A great use for abstract classes are where you want to have a class but don't want to create objects based on the class. By defining an abstract class we can create a blueprint for subclasses.
Following example demonstrates inheriting from an abstract class but having different constructors:
abstract class Shape
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
end
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end
crc = Circle.new(4)
rec = Rectangle.new(2, 4)
The following example is a more common pattern where we define an abstract method on the abstract class, thus making sure that the classes which implement from the abstract, implement the abstract methods. If they don't, you'll get a compilation error:
abstract class Shape
abstract def area : Number
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
def area : Number
MATH::PI * radius * 2
end
end
crc = Circle.new(12)
p crc.area # => 323......
# The following class won't compile because it doesn't implement
# area method:
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end