Inheritance is one of the core principles of Object-Oriented Programming (OOP) that allows a class to inherit properties and methods from another class. In Dart, inheritance helps to promote code reuse and establishes relationships between classes, making the code more maintainable and structured. This post will explore inheritance in Dart, how it works, and its practical uses with examples.
For further information on other topics of Dart, click here.
For an in-depth exploration of other OOP topics in Dart, such as inheritance, polymorphism, and encapsulation etc, click here.
What is Inheritance?
Inheritance in Dart allows one class (called the subclass or child class) to inherit the properties and methods of another class (called the superclass or parent class). This provides a way to create a hierarchy of classes, where the child class extends the functionality of the parent class without rewriting code.
Key Concepts of Inheritance in Dart:
- Superclass: The class that is being inherited from.
- Subclass: The class that inherits from the superclass.
- Method Overriding: Allows a subclass to provide a specific implementation of a method that is already defined in the superclass.
Basic Inheritance
In Dart, inheritance is implemented using the extends
keyword. When a class extends another class, it inherits all non-private members (properties and methods) of the superclass.
Example of Inheritance
class Animal {
void eat() {
print("Animal is eating");
}
}
class Dog extends Animal {
void bark() {
print("Dog is barking");
}
}
void main() {
var dog = Dog();
dog.eat(); // Inherited from Animal
dog.bark(); // Defined in Dog
}
Explanation:
In this example, the Dog
class inherits the eat()
method from the Animal
class. Additionally, Dog
defines its own method, bark()
. This demonstrates the basic idea of inheritance: reusing code from the parent class while adding specific functionality in the child class.
Method Overriding
In Dart, a subclass can override a method of the superclass. This is useful when the subclass needs to provide a specific implementation of a method that is already defined in the superclass.
Example of Method Overriding
class Animal {
void sound() {
print("Animal makes a sound");
}
}
class Cat extends Animal {
@override
void sound() {
print("Cat meows");
}
}
void main() {
var cat = Cat();
cat.sound(); // Output: Cat meows
}
Explanation:
In this example, the Cat
class overrides the sound()
method of the Animal
class using the @override
annotation. Although the Animal
class has a sound()
method, the Cat
class provides its own implementation.
super Keyword
The super
keyword in Dart is used to refer to the superclass and its members. It allows the child class to call the parent class’s constructor, methods, or properties.
Example of super
Keyword
class Vehicle {
void start() {
print("Vehicle started");
}
}
class Car extends Vehicle {
@override
void start() {
super.start(); // Calls the start method of Vehicle
print("Car started");
}
}
void main() {
var car = Car();
car.start();
// Output:
// Vehicle started
// Car started
}
Explanation:
In the Car
class, the start()
method first calls the start()
method of the Vehicle
class using super.start()
, and then it adds its own implementation. This is a great way to extend the functionality of a parent class method.
Constructors and Inheritance
When a subclass is instantiated, the constructor of the superclass is automatically called before the constructor of the subclass. You can explicitly call a superclass constructor using super()
in the subclass’s constructor.
Example of Constructor Inheritance
class Person {
String name;
Person(this.name);
void introduce() {
print("Hi, my name is $name");
}
}
class Employee extends Person {
String company;
Employee(String name, this.company) : super(name); // Calls the Person constructor
@override
void introduce() {
super.introduce(); // Calls the Person's introduce method
print("I work at $company");
}
}
void main() {
var emp = Employee("John", "Google");
emp.introduce();
// Output:
// Hi, my name is John
// I work at Google
}
Explanation:
In this example, the Employee
class inherits from the Person
class. The Employee
constructor explicitly calls the Person
constructor using super(name)
. Additionally, the introduce()
method of the Employee
class overrides the one in Person
but still calls the superclass version using super.introduce()
.
Inheritance and Private Variables
In Dart, private variables (those prefixed with an underscore _
) in a class are not inherited by subclasses. This ensures encapsulation of private data. Subclasses cannot directly access private members of their parent classes.
Example of Inheritance and Private Variables
class Parent {
String _secret = "This is private";
void showSecret() {
print(_secret);
}
}
class Child extends Parent {
void display() {
// print(_secret); // Error: '_secret' is private and cannot be accessed
print("Trying to access private data");
}
}
void main() {
var child = Child();
child.display(); // Output: Trying to access private data
}
Explanation:
In this case, the _secret
variable is private to the Parent
class, and the Child
class cannot directly access it. This preserves encapsulation even in an inheritance hierarchy.
Multiple Inheritance in Dart
Dart does not support multiple inheritance, meaning a class cannot inherit from more than one class. However, Dart supports a feature called Mixins, which allows you to reuse code from multiple sources.
Example of Multiple Inheritance Using Mixins
mixin CanFly {
void fly() {
print("Flying...");
}
}
class Bird with CanFly {}
void main() {
var bird = Bird();
bird.fly(); // Output: Flying...
}
Explanation:
Mixins allow a class like Bird
to use code from multiple sources, simulating the effect of multiple inheritance. In this example, Bird
can “inherit” the fly()
method from the CanFly
mixin.
Best Practices for Inheritance in Dart
-
Use Inheritance Wisely: Inheritance is a powerful tool, but it should be used when a true “is-a” relationship exists between the parent and child classes. For example, a
Dog
is anAnimal
, but aCar
is not aVehicle
in every case. - Avoid Deep Inheritance Chains: Having deep chains of inheritance can make the code difficult to maintain and understand. It’s better to keep inheritance hierarchies shallow.
- Leverage Composition Over Inheritance: In some cases, using composition (where objects are composed of other objects) can be more flexible than inheritance.
Conclusion
Inheritance is a fundamental concept in Dart‘s OOP structure, allowing classes to reuse code and share functionality. By using inheritance, method overriding, the super
keyword, and constructors, you can create structured and maintainable Dart applications. Additionally, understanding when to use inheritance and when to avoid it is key to writing clean, efficient code. For more advanced concepts and detailed explanations, be sure to check out our further articles on Dart OOP.
Happy Coding…!!!
Leave a Reply