On this page24 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is Inheritance?
- 2. Inherited Fields Are Real Fields
- 3. Adding New Members in the Subclass
- 4. The Subclass Constructor Must Call the Superclass Constructor
- 5. Super Parameters: super.name
- 6. The Superclass Is Built First
- 7. The Is-A Test
- 8. A Chain of Classes
- 9. Single Inheritance Only
- 10. is Checks in a Hierarchy
- 11. Object Is the Root of Every Class
- 12. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 59 — Inheritance
| Category | 10 — Advanced OOP |
| Lecture | 059 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
Two classes that share most of their fields used to mean writing everything twice. With
extends, a new class starts from an existing one and only adds what is different.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 052–058:
- creating classes with fields and methods, and objects from them
- constructors, including
this.nameparameters and named parameters withrequired this, getters and setters, and private members- the type test
isand the typeObject(Lectures 010–011)
Connection With Previous Lectures
In Category 9 we wrote one class at a time. Now suppose a school program needs a Person
and a Student:
class Person {
String name;
Person(this.name);
void greet() => print('Hi, I am $name');
}
class Student {
String name;
String school;
Student(this.name, this.school);
void greet() => print('Hi, I am $name');
}Student repeats name and greet() word for word. If we fix a bug in one greet(), we must
remember to fix the other. And Dart has no idea that a student is a person at all — to Dart
these are two unrelated classes. Inheritance fixes both problems.
Learning Objectives
By the end of this lecture you will be able to:
- Understand what inheritance is and the words superclass and subclass
- Create a subclass with
extends - Use fields and methods a subclass inherits from its superclass
- Add new fields and methods that only the subclass has
- Forward constructor values to the superclass with super parameters (
super.name) - Apply the is-a test to decide when inheritance fits
- Check types in a class hierarchy with
is - Explain why every class ultimately extends
Object
1. What Is Inheritance?
Inheritance lets one class take over all the fields and methods of another class, then add its own. The existing class is the superclass (or parent); the new one is the subclass (or child).
Analogy — every student is a person. Whatever is true of every person (they have a name, they can say hello) is automatically true of every student. A student just has a few extra things, such as a school.
class Person {
String name = 'Ali';
void greet() => print('Hi, I am $name');
}
class Student extends Person {}
void main() {
Student s = Student();
s.greet();
}Hi, I am AliThe syntax is class Subclass extends Superclass { … }. Student has an empty body, yet
s.greet() works: the method was inherited from Person.
2. Inherited Fields Are Real Fields
You read and change an inherited field on the subclass object exactly as if it had been written there.
class Person {
String name = 'Unknown';
int age = 0;
}
class Student extends Person {}
void main() {
Student s = Student();
s.name = 'Sara';
s.age = 18;
print('${s.name} is ${s.age}');
}Sara is 18Each Student object gets its own name and age.
3. Adding New Members in the Subclass
A subclass can add fields and methods the superclass knows nothing about. Its methods can use inherited fields freely.
class Person {
String name = 'Zayan';
void greet() => print('Hi, I am $name');
}
class Student extends Person {
String school = 'City School';
void study() => print('$name is studying at $school');
}
void main() {
Student s = Student();
s.greet();
s.study();
}Hi, I am Zayan
Zayan is studying at City SchoolInheritance only goes downwards. A plain Person does not get school:
// ✗ does not compile
// Person p = Person();
// print(p.school);Error: The getter 'school' isn't defined for the type 'Person'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'school'.
print(p.school);
^^^^^^4. The Subclass Constructor Must Call the Superclass Constructor
When the superclass has a constructor with required parameters, the subclass must pass those values up. Forgetting fails:
// class Person {
// String name;
// Person(this.name);
// }
//
// class Student extends Person {
// String school;
// Student(this.school); // ✗ who gives Person its name?
// }Error: The implicitly called unnamed constructor from 'Person' has required parameters.
Try adding an explicit super initializer with the required arguments.
Student(this.school);
^Every Student is also a Person, so the Person part has to be built too — and Person
cannot be built without a name.
5. Super Parameters: super.name
Since Dart 2.17, the fix is a super parameter. Where this.school stores a value in the
subclass's own field, super.name hands the value to the superclass constructor.
class Person {
String name;
Person(this.name);
}
class Student extends Person {
String school;
Student(super.name, this.school);
}
void main() {
Student s = Student('Ali', 'City School');
print('${s.name} attends ${s.school}');
}Ali attends City SchoolThe same works with named parameters. Write required super.name and the superclass must
take name as a named parameter too:
class Person {
String name;
Person({required this.name});
}
class Student extends Person {
String school;
Student({required super.name, required this.school});
}
void main() {
Student s = Student(name: 'Sara', school: 'Green Valley');
print('${s.name} attends ${s.school}');
}Sara attends Green ValleyCalling the superclass constructor by hand, and the super keyword in general, are the topic
of Lecture 061 — super.
6. The Superclass Is Built First
When you create a subclass object, the superclass constructor runs before the subclass constructor's body.
class Person {
String name;
Person(this.name) {
print('Person part ready: $name');
}
}
class Student extends Person {
String school;
Student(super.name, this.school) {
print('Student part ready: $school');
}
}
void main() {
Student('Zayan', 'City School');
}Person part ready: Zayan
Student part ready: City SchoolThis order is why the subclass body can safely use name: the Person part already exists.
7. The Is-A Test
Use inheritance only when the sentence "a B is an A" is true. "A student is a person", "a dog is an animal", "a savings account is a bank account" — all good.
"A car is an engine" is false. A car has an engine, so Engine should be a field inside
Car, not its superclass. We will compare these two designs in Lecture 066 — Composition vs
Inheritance.
8. A Chain of Classes
A subclass can itself be extended. Each level inherits everything above it.
class Person {
String name;
Person(this.name);
}
class Student extends Person {
String school;
Student(super.name, this.school);
}
class GraduateStudent extends Student {
String thesis;
GraduateStudent(super.name, super.school, this.thesis);
}
void main() {
var g = GraduateStudent('Sara', 'State University', 'Clean Water');
print('${g.name}, ${g.school}, thesis: ${g.thesis}');
}Sara, State University, thesis: Clean WaterGraduateStudent passes name and school up to Student, which passes name up to
Person. This family of related classes is called a class hierarchy.
9. Single Inheritance Only
A Dart class can have exactly one superclass. Listing two is rejected:
// class Person {}
// class Worker {}
// class Student extends Person, Worker {} // ✗Error: Each class definition can have at most one extends clause.
Try choosing one superclass and define your class to implement (or mix in) the others.
class Student extends Person, Worker {}
^The alternatives it mentions come in Lecture 063 — Interfaces and Lecture 064 — Mixins.
10. is Checks in a Hierarchy
Because a student is a person, is Person is true for a Student object. The reverse is
not true. The variables below are typed Object, so Dart must look at the actual values.
class Person {
String name;
Person(this.name);
}
class Student extends Person {
String school;
Student(super.name, this.school);
}
void main() {
Object s = Student('Ali', 'City School');
Object p = Person('Sara');
print(s is Student);
print(s is Person);
print(p is Student);
}true
true
falseFor the same reason, a Student can be stored in a Person variable. Through that variable
Dart only allows Person members — until an is check proves more:
class Person {
String name;
Person(this.name);
}
class Student extends Person {
String school;
Student(super.name, this.school);
}
void main() {
Person p = Student('Zayan', 'City School');
print(p.name);
if (p is Student) {
print(p.school); // allowed: p is known to be a Student here
}
}Zayan
City School11. Object Is the Root of Every Class
A class with no extends still has a superclass: Object. Writing class Person {} means
exactly class Person extends Object {}. That is why every object passes is Object and
answers to toString() and runtimeType — they are inherited from Object.
class Person {}
class Student extends Person {}
void main() {
Student s = Student();
print(s.runtimeType);
print(s.toString());
}Student
Instance of 'Student'Instance of 'Student' is Object's default text. Replacing an inherited method with your
own version is the next lecture, Lecture 060 — Method Overriding.
12. A Real-World Example
class Account {
String owner;
double balance;
Account(this.owner, this.balance);
void deposit(double amount) => balance = balance + amount;
void show() => print('$owner: $balance');
}
class SavingsAccount extends Account {
double rate;
SavingsAccount(super.owner, super.balance, this.rate);
void addInterest() => deposit(balance * rate);
}
void main() {
List<Account> accounts = [
Account('Ali', 1000),
SavingsAccount('Sara', 2000, 0.05),
];
for (var account in accounts) {
account.deposit(500);
if (account is SavingsAccount) {
account.addInterest();
}
account.show();
}
}Ali: 1500.0
Sara: 2625.0SavingsAccount inherits four members, adds two, and forwards two values with super
parameters. One List<Account> holds both kinds, and is decides who earns interest.
Code Examples
Example 1 — An Empty Subclass
class Vehicle {
int wheels = 4;
void describe() => print('A vehicle with $wheels wheels');
}
class Car extends Vehicle {}
void main() {
Car car = Car();
car.describe();
}A vehicle with 4 wheelsExample 2 — Inherited Plus New
class Animal {
String name = 'Rex';
void eat() => print('$name is eating');
}
class Dog extends Animal {
void bark() => print('$name says Woof!');
}
void main() {
Dog d = Dog();
d.eat();
d.bark();
}Rex is eating
Rex says Woof!Example 3 — Changing an Inherited Field
class Animal {
String name = 'Unknown';
}
class Cat extends Animal {}
void main() {
Cat c = Cat();
c.name = 'Milo';
print(c.name);
}MiloExample 4 — Positional Super Parameter
class Shape {
String color;
Shape(this.color);
}
class Square extends Shape {
double side;
Square(super.color, this.side);
double area() => side * side;
}
void main() {
Square sq = Square('red', 3);
print('${sq.color} square, area ${sq.area()}');
}red square, area 9.0Example 5 — Named Super Parameters
class Product {
String title;
double price;
Product({required this.title, required this.price});
}
class Book extends Product {
String author;
Book({required super.title, required super.price, required this.author});
}
void main() {
Book b = Book(title: 'Dart Basics', price: 25, author: 'Sara');
print('${b.title} by ${b.author}: ${b.price}');
}Dart Basics by Sara: 25.0Example 6 — Type Checks
class Animal {}
class Dog extends Animal {}
void main() {
Object a = Animal();
Object d = Dog();
print(d is Animal);
print(a is Dog);
print(a is Animal);
}true
false
trueExample 7 — Student Marks
class Person {
String name;
Person(this.name);
}
class Student extends Person {
List<int> marks;
Student(super.name, this.marks);
int total() => marks.fold(0, (sum, m) => sum + m);
}
void main() {
Student s = Student('Ali', [80, 75, 85]);
print('${s.name}: ${s.total()}');
}Ali: 240How it works
super.namesends'Ali'toPerson;this.marksstores the list inStudent.total()exists only onStudentand adds the marks withfold(Lecture 044).s.nameis inherited;s.total()is the subclass's own method.
Example 8 — Shopping Total with a Discounted Product
class Product {
String title;
double price;
Product(this.title, this.price);
}
class DiscountedProduct extends Product {
double discount;
DiscountedProduct(super.title, super.price, this.discount);
double finalPrice() => price - price * discount;
}
void main() {
Product pen = Product('Pen', 50);
DiscountedProduct bag = DiscountedProduct('Bag', 1200, 0.25);
double total = pen.price + bag.finalPrice();
print('Total: $total');
}Total: 950.0How it works
baggetstitleandpricefromProductand addsdiscount.finalPrice()reads the inheritedprice:1200 - 300 = 900.- The pen adds
50, giving950.0.
Example 9 — Bank Balance
class Account {
String owner;
double balance;
Account(this.owner, this.balance);
void withdraw(double amount) => balance = balance - amount;
}
class SavingsAccount extends Account {
double rate;
SavingsAccount(super.owner, super.balance, this.rate);
void addInterest() => balance = balance + balance * rate;
}
void main() {
SavingsAccount acc = SavingsAccount('Zayan', 1000, 0.10);
acc.withdraw(200);
acc.addInterest();
print('${acc.owner}: ${acc.balance}');
}Zayan: 880.0How it works
withdraw()is inherited and takes the balance from1000to800.addInterest()is new and adds 10%:800 + 80 = 880.- Both methods change the same
balancefield.
Example 10 — Temperature Sensors
class Sensor {
String location;
Sensor(this.location);
}
class Thermometer extends Sensor {
double celsius;
Thermometer(super.location, this.celsius);
double fahrenheit() => celsius * 9 / 5 + 32;
}
void main() {
List<Sensor> sensors = [Sensor('Door'), Thermometer('Kitchen', 25)];
for (var s in sensors) {
if (s is Thermometer) {
print('${s.location}: ${s.fahrenheit()} F');
} else {
print('${s.location}: no reading');
}
}
}Door: no reading
Kitchen: 77.0 FHow it works
- The list's type is
List<Sensor>, so it may hold aThermometertoo. - Inside
if (s is Thermometer), Dart allowsfahrenheit(). - The plain
Sensorfalls intoelse.
Common Beginner Mistakes
1. Not passing values to the superclass constructor
// class Student extends Person {
// String school;
// Student(this.school);
// }Error: The implicitly called unnamed constructor from 'Person' has required parameters.Forward the superclass's values with super.name.
2. Using this. for an inherited field
// class Student extends Person {
// String school;
// Student(this.name, this.school); // ✗ name lives in Person
// }Error: 'name' isn't an instance field of this class.this. only works for fields declared in the class itself. Use super.name.
3. Mixing positional and named styles
// class Person {
// String name;
// Person({required this.name}); // named
// }
//
// class Student extends Person {
// String school;
// Student(super.name, this.school); // ✗ positional
// }Error: The super constructor has no corresponding positional parameter.If the superclass takes name by name, write required super.name inside { }.
4. Expecting the superclass to have subclass members
// Person p = Student('Ali', 'City School');
// print(p.school); // ✗Error: The getter 'school' isn't defined for the type 'Person'.Check if (p is Student) first, or store it in a Student variable.
5. Putting a superclass object in a subclass variable
// Student s = Person('Ali'); // ✗Error: A value of type 'Person' can't be assigned to a variable of type 'Student'.Every student is a person, but not every person is a student.
6. Inheriting where "has-a" fits
class Car extends Engine compiles but makes no sense. Use a field instead.
Best Practices
- Apply the is-a test before writing
extends. - Put shared fields and methods in the superclass once; put only differences in subclasses.
- Prefer super parameters (
super.name) over repeating a field in the subclass. - Keep hierarchies shallow — two or three levels are usually enough.
- Use
ischecks sparingly; reach for them only when one kind needs extra handling.
Mini Challenge
Create a class Employee with a name and a salary (double), set through a positional
constructor, and a method show() that prints name: salary. Create a subclass Manager
with an extra bonus field and a method totalPay() returning salary plus bonus. Build a
manager named 'Sara' with salary 5000 and bonus 1500, call show(), then print the
total pay.
Your program should print exactly:
Sara: 5000.0
Total pay: 6500.0Try it yourself before opening the solution.
Show solution
class Employee {
String name;
double salary;
Employee(this.name, this.salary);
void show() => print('$name: $salary');
}
class Manager extends Employee {
double bonus;
Manager(super.name, super.salary, this.bonus);
double totalPay() => salary + bonus;
}
void main() {
Manager m = Manager('Sara', 5000, 1500);
m.show();
print('Total pay: ${m.totalPay()}');
}Sara: 5000.0
Total pay: 6500.0How it works
Managerforwardsnameandsalarywith super parameters and storesbonusitself.show()is inherited unchanged.totalPay()is new and reads the inheritedsalary.
Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Create a class
Birdwith a methodfly()that printsFlying. Create an empty subclassParrot, make aParrot, and callfly(). - Give
Animala fieldname = 'Leo'. CreateLion extends Animalwith a methodroar()that printsLeo roars, using the inherited field. - With
class Animal {},class Dog extends Animal {},Object a = Animal();andObject d = Dog();, print the results ofd is Animal,a is Dog, andd is Dog.
Application
class Person { String name; Person(this.name); }is given. WriteTeacher extends Personwith asubjectfield and a constructor usingsuper.name. PrintAli teaches Math.- Rewrite question 4 with named parameters in both classes, creating
Teacher(name: 'Sara', subject: 'Science'), and printSara teaches Science. - Give
Persona constructor body that printsPerson builtandTeacherone that printsTeacher built. Create oneTeacherand show the order. - Create
Product(title, price)andFood extends Productwith anexpirystring. PrintMilk: 120.0 (expires Friday). - Store a
Teacher('Zayan', 'Art')in aPersonvariable. Print the name, then use anischeck to print the subject.
Challenge
- Build
Vehicle(wheels)→Car(wheels, brand)→ElectricCar(wheels, brand, range)with super parameters. CreateElectricCar(4, 'Volt', 400)and printVolt: 4 wheels, 400 km. - Using
Account(owner, balance)withdeposit()andSavingsAccount(owner, balance, rate)withaddInterest(), putAccount('Ali', 500)andSavingsAccount('Sara', 1000, 0.10)in oneList<Account>. Deposit100into every account, add interest only to savings accounts, and print eachowner: balance.
Practice Answers
Question 1 — Solution
class Bird {
void fly() => print('Flying');
}
class Parrot extends Bird {}
void main() {
Parrot p = Parrot();
p.fly();
}FlyingQuestion 2 — Solution
class Animal {
String name = 'Leo';
}
class Lion extends Animal {
void roar() => print('$name roars');
}
void main() {
Lion l = Lion();
l.roar();
}Leo roarsQuestion 3 — Solution
class Animal {}
class Dog extends Animal {}
void main() {
Object a = Animal();
Object d = Dog();
print(d is Animal);
print(a is Dog);
print(d is Dog);
}true
false
trueQuestion 4 — Solution
class Person {
String name;
Person(this.name);
}
class Teacher extends Person {
String subject;
Teacher(super.name, this.subject);
}
void main() {
Teacher t = Teacher('Ali', 'Math');
print('${t.name} teaches ${t.subject}');
}Ali teaches MathQuestion 5 — Solution
class Person {
String name;
Person({required this.name});
}
class Teacher extends Person {
String subject;
Teacher({required super.name, required this.subject});
}
void main() {
Teacher t = Teacher(name: 'Sara', subject: 'Science');
print('${t.name} teaches ${t.subject}');
}Sara teaches ScienceQuestion 6 — Solution
class Person {
String name;
Person(this.name) {
print('Person built');
}
}
class Teacher extends Person {
String subject;
Teacher(super.name, this.subject) {
print('Teacher built');
}
}
void main() {
Teacher('Ali', 'Math');
}Person built
Teacher builtThe superclass part is always built first.
Question 7 — Solution
class Product {
String title;
double price;
Product(this.title, this.price);
}
class Food extends Product {
String expiry;
Food(super.title, super.price, this.expiry);
}
void main() {
Food milk = Food('Milk', 120, 'Friday');
print('${milk.title}: ${milk.price} (expires ${milk.expiry})');
}Milk: 120.0 (expires Friday)Question 8 — Solution
class Person {
String name;
Person(this.name);
}
class Teacher extends Person {
String subject;
Teacher(super.name, this.subject);
}
void main() {
Person p = Teacher('Zayan', 'Art');
print(p.name);
if (p is Teacher) {
print(p.subject);
}
}Zayan
ArtQuestion 9 — Solution
class Vehicle {
int wheels;
Vehicle(this.wheels);
}
class Car extends Vehicle {
String brand;
Car(super.wheels, this.brand);
}
class ElectricCar extends Car {
int range;
ElectricCar(super.wheels, super.brand, this.range);
}
void main() {
ElectricCar e = ElectricCar(4, 'Volt', 400);
print('${e.brand}: ${e.wheels} wheels, ${e.range} km');
}Volt: 4 wheels, 400 kmQuestion 10 — Solution
class Account {
String owner;
double balance;
Account(this.owner, this.balance);
void deposit(double amount) => balance = balance + amount;
}
class SavingsAccount extends Account {
double rate;
SavingsAccount(super.owner, super.balance, this.rate);
void addInterest() => deposit(balance * rate);
}
void main() {
List<Account> accounts = [
Account('Ali', 500),
SavingsAccount('Sara', 1000, 0.10),
];
for (var a in accounts) {
a.deposit(100);
if (a is SavingsAccount) {
a.addInterest();
}
print('${a.owner}: ${a.balance}');
}
}Ali: 600.0
Sara: 1210.0Sara's account reaches 1100 after the deposit, and 10% interest adds 110.
Quick Revision
- What do a superclass and a subclass mean?
- Which keyword creates a subclass?
- Can a superclass use a field declared only in its subclass?
- What does
super.namedo in a constructor? - Which constructor body runs first: the superclass's or the subclass's?
- What is the is-a test?
- How many classes can one class extend?
- What is the superclass of a class written with no
extends?
Key Takeaways
class B extends AmakesBa subclass that inherits every field and method ofA.- A subclass can add its own fields and methods; the superclass never gains them.
- If the superclass constructor needs values, the subclass must forward them — super
parameters (
super.name) do this in one word. - The superclass part of an object is built before the subclass part.
- Use inheritance only when "a B is an A" is true; "has-a" calls for a field.
- A Dart class has exactly one superclass, and chains of classes form a hierarchy.
- A subclass object passes
ischecks for every class above it, and anischeck unlocks subclass members. - Every class extends
Objectin the end, which is wheretoString()andruntimeTypecome from.
What's Next?
Lecture 60 — Method Overriding. A subclass can now add new methods, but it still has to
accept every inherited method exactly as written — toString() still says
Instance of 'Student'. Next we will learn how a subclass replaces an inherited method with its
own version, and what @override tells Dart.