Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 11

Inheritance and Overriding

Imagine you are describing animals to a friend. You could say: a dog has four legs, breathes air, and barks. A cat has four legs, breathes air, and meows. Notice how much you repeated. Inheritance is a way to avoid that repetition in code. You describe what is shared once, and then only what is diff
Level: GeneralLength: In depth4 min read

Read at another depth

Inheritance and Overriding

What Is Inheritance, Really?

Imagine you are describing animals to a friend. You could say: a dog has four legs, breathes air, and barks. A cat has four legs, breathes air, and meows. Notice how much you repeated. Inheritance is a way to avoid that repetition in code. You describe what is shared once, and then only what is different for each specific kind.

In Dart, a class is a blueprint for objects — things that hold data and do work. Inheritance means one class (the child) gets the fields and methods of another class (the parent) for free. The child can then add its own, or change the ones it inherited.

Why does this matter? Because real programs are full of things that share most of their behavior. Without inheritance, you would copy the shared parts into many places. When something changes, you would have to fix it everywhere. Inheritance lets you fix it once.

A First Example

Let's model vehicles. Every vehicle can describe itself. A car adds doors. A bicycle adds a bell.

Code example in dart
class Vehicle {
  final String name;

  Vehicle(this.name);

  String describe() => '$name is a vehicle.';
}

class Car extends Vehicle {
  final int doors;

  Car(String name, this.doors) : super(name);

  @override
  String describe() => '$name is a car with $doors doors.';
}

void main() {
  final car = Car('Bessie', 4);
  print(car.describe()); // Bessie is a car with 4 doors.
}

Three new things are happening here:

  1. extends Vehicle makes Car a child of Vehicle. Every car is a vehicle.
  2. super(name) passes the name up to the parent's constructor. A child must let the parent set up its part of the object.
  3. @override marks that Car replaces describe() with its own version. This marker is optional in Dart but strongly recommended — the compiler will warn you if your spelling does not actually match a real method.

Overriding: Changing What You Inherited

Overriding is when a child supplies its own version of a method the parent already has. The child's version wins when you call the method on a child object.

You can also call the parent's version from inside the override using super.methodName(). This is handy when you want to add to the parent's behavior rather than replace it.

Code example in dart
class Bicycle extends Vehicle {
  Bicycle(String name) : super(name);

  @override
  String describe() {
    final base = super.describe(); // run the parent's version
    return '$base Ring ring!';
  }
}

void main() {
  print(Bicycle('Pegasus').describe());
  // Pegasus is a vehicle. Ring ring!
}

What You Can and Cannot Override

Dart has firm rules here, and knowing them prevents real bugs:

  • You cannot override a method with the wrong types. If the parent returns String, you must return String (or something compatible).
  • You cannot override a field declared final — it can only be set once, in the constructor.
  • You can override a getter even if the parent has a field, because in Dart a field and a getter with the same name are the same thing as far as callers are concerned.
  • Private members (names starting with _) belong to the file where they are written. A child class in a different file cannot see or override them.

The Substitution Idea

Here is the mental model that makes inheritance click: wherever the parent type is expected, a child object can stand in. A function asking for a Vehicle will happily accept a Car or a Bicycle, because both are vehicles.

Code example in dart
void printDescription(Vehicle v) {
  print(v.describe());
}

void main() {
  printDescription(Car('Bessie', 4));
  printDescription(Bicycle('Pegasus'));
}

When printDescription calls v.describe(), Dart looks at the actual object at runtime, not the declared type. This is called dynamic dispatch, and it is what makes overriding useful. The variable is typed as Vehicle, but the car's version of describe runs.

Adding to the Chain: Grandchildren

Inheritance can go deeper than one level. A SportsCar can extend Car, which extends Vehicle. Each level inherits from everything above it and may override again.

A diagram with Vehicle at the top, an arrow down to Car, and an arrow from Car down to SportsCar, showing three levels of inheritance.
100%
A small inheritance chain, from parent to grandchild
Show diagram source
classDiagram
    Vehicle <|-- Car
    Car <|-- SportsCar
    Vehicle : +String name
    Vehicle : +describe() String
    Car : +int doors
    Car : +describe() String
    SportsCar : +int topSpeed
    SportsCar : +describe() String

Each class down the chain must call its parent's constructor with super(...). Forgetting this is one of the most common beginner errors in Dart.

When Not to Inherit

Inheritance is powerful but easy to overuse. Two warnings worth remembering:

  • Inheritance is for "is-a" relationships. A car is a vehicle, so extending is right. A car has an engine — that is a part, not a kind — so the engine should be a separate object stored in a field, not a parent class.
  • Deep chains get fragile. If you change the middle of a chain, every class below it changes too. In later chapters you will meet mixins and abstract classes, which offer safer ways to share behavior.

A good rule of thumb: prefer sharing through composition (holding an object in a field) unless the "is-a" relationship is obvious.

Pulling It Together

  • extends makes one class a child of another.
  • The child inherits all visible fields and methods.
  • super(...) in the constructor passes work to the parent.
  • @override lets the child replace a method, and super.method() lets it extend it.
  • A child object can be used anywhere the parent type is expected.
  • Dart picks the child's method at runtime — that is the point of it all.

Exercise

If SportsCar extends Car and you forget to write super(...) in its constructor, what happens?

Show answer

The program will not compile. Every subclass constructor must eventually call its parent's constructor, and super(...) is how you do it. Dart reports an error telling you the superclass constructor was never invoked.

Exercise

A function takes a Vehicle parameter and calls v.describe(). You pass it a Bicycle. Which describe runs, and why?

Show answer

Bicycle's version. Dart uses dynamic dispatch: it looks at the actual object at runtime, not the declared parameter type, so the child's override wins.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Inheritance and Overriding

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that