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 12

Abstract Classes and Interfaces

When you start building bigger programs, you often want to say: "these things all share an ability, but each one does it differently." This chapter shows how Dart handles that promise with two tools: **abstract classes** and **interfaces**.
Level: GeneralLength: In depth4 min read

Read at another depth

Abstract Classes and Interfaces

When you start building bigger programs, you often want to say: "these things all share an ability, but each one does it differently." This chapter shows how Dart handles that promise with two tools: abstract classes and interfaces.

The Problem: Promises Without Details

Imagine a drawing program. It has circles, squares, and triangles. Every shape can compute its area — the space it covers — but the math is different for each one. The program itself doesn't care how a shape computes its area; it only needs to know that it can.

This is where an abstract class helps. An abstract class is a class that is never created directly. Instead, it exists to describe what its children must be able to do. It can include:

  • Abstract methods — a method name and its input/output types, but no body. A child class must fill in the body.
  • Concrete methods — normal methods with full bodies, shared by all children.

An abstract class is like a restaurant menu: it lists what must be available, but someone else (each child class) must actually cook the dishes.

Your First Abstract Class

The abstract keyword marks the class. Note that an abstract method has no braces — just a semicolon.

Code example in dart
abstract class Shape {
  double area(); // abstract: no body, children must provide one

  // concrete: shared by all children
  String describe() => 'A shape with area ${area().toStringAsFixed(2)}';
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);

  @override
  double area() => 3.14159 * radius * radius;
}

class Square implements Shape {
  final double side;
  Square(this.side);

  @override
  double area() => side * side;
}

void main() {
  final shapes = [Circle(2), Square(3)];
  for (final s in shapes) {
    print(s.describe()); // works on any Shape
  }
}

Running this prints:

Code example in text
A shape with area 12.57
A shape with area 9.00

The list holds both kinds of shapes side by side. The loop treats them the same, because both keep the promise of area().

Interfaces: Just a Contract

Here is a surprise about Dart: every class is also an interface. There is no special interface keyword needed. When you write implements Shape, you are saying "I will follow the Shape contract" — the list of method names and types that Shape publishes.

The implements keyword means: I promise to provide my own version of every member, including fields. Nothing is inherited from the parent — not even working code. You get only the contract.

Compare that with extends, which you met in the inheritance chapter. extends means "I am a child, and I inherit working code from my parent." You then choose which methods to override.

  • extends: inherit code, override what you like.
  • implements: inherit nothing, promise everything.

Choosing Between extends and implements

This is the main decision you make here. Use the table to pick.

You want...Use
To reuse a parent's working code`extends`
To keep a parent's state (fields)`extends`
Only a shared contract, fully your own code`implements`
To promise behavior across unrelated classes`implements`
A mix: some code shared, some required`extends` an abstract class
Code example in dart
abstract class Logger {
  void log(String message); // must be filled in

  // shared convenience, built on the abstract method
  void warn(String message) => log('WARN: $message');
}

class ConsoleLogger extends Logger {
  @override
  void log(String message) => print(message);
}

void main() {
  final logger = ConsoleLogger();
  logger.warn('disk almost full'); // prints: WARN: disk almost full
}

Because ConsoleLogger extends Logger, it only needs to write log; it inherits warn for free. That is the sweet spot of abstract classes: required methods plus free shared code.

Runtime Type Checks

Sometimes you receive a general value — say a Shape — and need to know what it really is. Dart lets you check at runtime with is, and type promotion (the compiler narrowing the type afterward) gives you access to the more specific members.

Code example in dart
void printShape(Shape s) {
  if (s is Circle) {
    // promoted: the compiler now knows s is a Circle
    print('circle with radius ${s.radius}');
  }
}

This check is real and safe. If s is not a Circle, the block simply does not run — no crash, no guessing.

A diagram with the abstract class Shape at the top listing area and describe as members. Two arrows lead down from Shape, one to Circle with member radius and area, one to Square with member side and area. Both child classes use the implements relationship, meaning they promise every member of Shape.
100%
The Shape hierarchy — an abstract contract with two implementers
Show diagram source
classDiagram
    class Shape {
        <<abstract>>
        +area() double
        +describe() String
    }
    class Circle {
        +radius double
        +area() double
    }
    class Square {
        +side double
        +area() double
    }
    Shape <|.. Circle : implements
    Shape <|.. Square : implements

Why Not Just Use Plain Classes?

You could make Shape a normal class and have children inherit. But then nothing stops someone from creating Shape() directly — a shape with no actual math behind it, whose area() does something meaningless. Declaring the class abstract turns that mistake into an error the compiler catches before the program ever runs. Catching mistakes early is the whole point.

The same reasoning applies to interfaces. If a function accepts a Shape, the compiler guarantees whatever you pass can compute an area. Without the contract, you would find out about the missing ability at the worst time — while the program is running.

Common Errors and How to Fix Them

  • Error: Concrete classes must implement... — you used implements but forgot to write a method from the contract. Add it, ideally with @override above it so the checker confirms the signature matches.
  • Trying to create an abstract class — Shape() fails. Create a child class instead.
  • Expecting inherited code with implements — implements gives you the contract only. If you want the parent's code, use extends.
  • Signature mismatch — if your override has different parameter types, the compiler complains. Copy the parent's signature exactly, or let your editor insert the override for you.

Where This Fits in Your Dart Toolkit

Abstract classes and interfaces are the foundation for everything that shares behavior. In the next chapters you will meet mixins, which share behavior without any parent-child relationship at all, and generics, which make contracts like Shape reusable for many types at once. Together these tools let you describe what your code promises and keep how it does it open to change — the heart of flexible programs.

Exercise

What is the key difference between extends and implements when relating to a parent class?

Show answer

extends inherits the parent's working code and fields, and lets you override only what you choose. implements inherits nothing; it makes you promise to provide your own version of every member of the parent's interface, including fields.

Exercise

Why would you mark the Shape class abstract instead of leaving it a normal class?

Show answer

To prevent creating useless instances directly and to force every child class to fill in the abstract methods like area(). The compiler then catches incomplete children before the program runs.

Exercise

In the code if (s is Circle) print(s.radius); why can the compiler allow access to s.radius inside the block?

Show answer

Because of type promotion. The is check confirms s is a Circle, so inside the block the compiler treats s as a Circle and allows access to its members.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Abstract Classes and Interfaces

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