The ability of a class to inherit from another class is called inheritance. Inheritance is one of the most important features of OOP.
Sub Class: The class that inherits from another class is called a sub-class or derived class or child class.
Super Class: The class that inherited its attributes to another class is called a base class, a super-class or parent class
In other words we could say that inheritance is including the content of one class to another. Which means the other class will get
the functions and variables from the first class.
As an example, assume there is a set of vehicles, and you need to create classes for buses, cars, and trucks. The methods
`fuelAmount`, `capacity`, and `useBrakes` will be the same for all three classes. So if we create these classes without using
inheritance concept, we should write those three methods in each class. This, however, will lead to repeating the same code 3
times. To avoid this, we can use inheritance by creating a special class to represent a vehicle and writing these functions in
it, then make the other classes inherit from it.
Inheritance in Alusus can be somehow strange because Alusus is low-level language (even lower than C++) so it provides the user with the basic building blocks that can be used to apply inheritance concepts (in addition to other things). The main building block that we use to apply inheritance is `@injection` modifier. To define a class that inherits from another class in Alusus, all you have to do is defining a variable from the parent class at the beginning of the child class and add `@injection` modifier to it. In this way we inherit the parent class.
class Sub_Class { @injection def super_class: Super_Class; }
To understand the above definition, we need to understand the way programming languages apply inheritance behind the scenes. When you define a derived class from another class in languages like C++, what the compiler does is adding an element from the parent to the child (includes the parent in the child) and then makes the elements of the parent available in the child for direct access as if it was defined in the child class not in a variable inside the child class. This is exactly what we did here. We told the compiler to add the parent class as an element inside the child class and we told the compiler using `@injection` modifier to inject all definitions from the parent scope inside the enclosing scope (the child scope here). In other words, if there is an element in the parent names `element1` then the compiler will convert it automatically into `child.parent.element1`. This is simply what other compilers do, and we are telling the compiler here to do that explicitly. Later we can add helper commands or macros to make this definition easier. Or the user can do that himself.
In Alusus you can apply any type of inheritance using the simple concept mentioned above.
Class `B` inherits the attributes of class `A`.
In the next example we define a class called `Parent` that contains a variable `x` and a function `printMessage`.
And we define the class `Child` that contains a variable `y` and inherits from `Parent`.
// Single inheritance import "Srl/Console.alusus"; use Srl; // define the parent class. class Parent { def x: int = 5; handler this.printMessage() { Console.print("Hello from class Parent \n"); } } // define the child class. class Child { @injection def obj: Parent; def y: int = 8; } // create an object from the child class. def objChild: Child; // print the variable `y` which is define in the child class. Console.print("y = %d\n", objChild.y); // print the variable `x` which is defined in the parent class and inherited by the child class. Console.print("x = %d\n", objChild.x); // calling `printMessage` function which is defined in the parent class and inherited by the child class. objChild.printMessage(); /* y = 8 x = 5 Hello from class Parent */
In Alusus, the priority of accessing variables and function is given to the current scope before the other scopes. In other words, if
there is a function `get` in the parent and another function with the same name in the child, and it is called in the child then
the `get` function in the child is called, not the one in the parent.
The next example clarify this idea:
import "Srl/Console.alusus"; use Srl.Console; class Parent { def x: int = 5; handler this.printMessage() { print("Hello from class Parent \n"); } } class Child { @injection def parent: Parent; def y: int = 8; handler this.printMessage() { print("Hello from class Child \n"); } } def objChild: Child; objChild.printMessage(); /* Hello from class Child */ // note that it will print the message in the child not the parent // because the current scope has higher priority.
Class `A` is inherited by class `B`, and class `B` is inherited by class `C` (a derived class inherits a derived class).
In the next example, we will define `Vehicle` class, then class `FourWheeler` will inherit it, and finally class `Car` will inherit
`FourWheeler` class.
// Multilevel Inheritance import "Srl/Console.alusus"; use Srl.Console; class Vehicle { handler this~init() { print("This is a Vehicle\n"); } } // a child class inheriting from the previous class. class FourWheeler { @injection def vehicle: Vehicle; handler this~init() { print("Objects with 4 wheels are vehicles\n"); } } // a child class inheriting from the FourWheeler class (which in turn inherits from `Vehicle` class). class Car { @injection def fourWheeler: FourWheeler; handler this~init() { print("Car has 4 Wheels"); } } // func main{ def objCar: Car; // creating an object from `Car` class will call the parents constructors also. } main() /* This is a Vehicle Objects with 4 wheels are vehicles Car has 4 Wheels */
Note that creating an object from class `Car` leads to calling the constructor of `FourWheeler` class, which in turn leads to calling the constructor of `Vehicle` class.
A class `C` inherits from class `A` and class `B`.
In the next example, class `Car` will inherit from both `Vehicle` and `FourWheeler`.
import "Srl/Console.alusus"; use Srl.Console; class Vehicle { handler this~init() { print("This is a Vehicle\n"); } } class FourWheeler { handler this~init() { print("Objects with 4 wheels are vehicles\n"); } } // Class `Car` inherit from both `Vehicle` and `FourWheeler`. class Car { @injection def fourWheeler: FourWheeler; @injection def vehicle: Vehicle; handler this~init() { print("Car has 4 Wheels"); } }; func main{ def objCar:Car; } main(); /* Objects with 4 wheels are vehicles This is a Vehicle Car has 4 Wheels */
Note that it called the constructor of `FourWheeler` first. This is reasonable since we inherit it first.
Several classes inherits from the same class. For example, class `C` and class `B` inherit from class `A`.
In the next example, class `Vehicle` is inherited by both `Car` and `FourWheeler` classes.
import "Srl/Console.alusus"; use Srl.Console; class Vehicle { handler this~init() { print("This is a Vehicle\n"); } } // `FourWheeler` class inherits from `Vehicle` class. class FourWheeler { @injection def obj: Vehicle; handler this~init() { print("Objects with 4 wheels are vehicles\n"); } } // `Car` class inherits from `Vehicle` class. class Car { @injection def obj: Vehicle; handler this~init() { print("Car has 4 Wheels"); } } func main { def obj1: FourWheeler; def obj2: Car; } main(); /* This is a Vehicle Objects with 4 wheels are vehicles This is a Vehicle Car has 4 Wheels */
You can combine between the different types of inheritance.
import "Srl/Console.alusus"; use Srl.Console; class Vehicle { handler this~init() { print("This is a Vehicle\n"); } } class Fare { handler this~init() { print("Fare of Vehicle\n"); } } // class `Car` inherits from `Vehicle`. class Car { @injection def obj:Vehicle; handler this~init() { print("Car has 4 Wheels"); } } // class `Bus` inherits from `Vehicle` and `Fare`. class Bus { @injection def obj: Vehicle; @injection def obj: Fare; }; func main { def obj: Bus; } main(); /* This is a Vehicle Fare of Vehicle */