On this page22 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What an Interface Is
- 2. Implementing a Class with implements
- 3. No Code Is Inherited
- 4. Fields Are Part of the Interface
- 5. Implementing Several Interfaces
- 6. An Abstract Class as a Pure Interface
- 7. The Object Has the Interface's Type
- 8. extends vs implements
- 9. Combining extends and implements
- 10. 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 63 — Interfaces
| Category | 10 — Advanced OOP |
| Lecture | 063 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
A class can extend only one parent. With
implements, a class promises to do everything another class can do — for as many classes as it likes — and writes the code itself.
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–062:
- classes, fields, methods, constructors, getters, and setters
extends,@override, andsuperischecks and parent-typed variables- abstract classes and abstract methods
Connection With Previous Lectures
In Lecture 059 we learned that a Dart class has exactly one superclass. Now picture an office photocopier. It prints and it scans. We already have both classes:
class Printer {
void printPage(String text) {
print('Printing: $text');
}
}
class Scanner {
void scan() {
print('Scanning...');
}
}
class Photocopier extends Printer, Scanner {}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.Dart refuses — and the message itself points to the answer: implement the others.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what an interface is and why every Dart class has one
- Make a class follow an interface with
implements - Explain why
implementsinherits no code at all - Provide fields, getters, and setters that an interface requires
- Implement several interfaces in one class
- Use an abstract class as a pure interface
- Choose between
extendsandimplements, and combine them in one class - Fix the errors Dart reports when an implementation is incomplete
1. What an Interface Is
An interface is the list of public members a class offers — the names of its fields, getters, setters, and methods, with their types. It says what an object can do, not how.
Analogy — a job advert lists duties: "answers phones, books meetings". Anyone who can do every duty can fill the job, however they learned it. The advert is the interface; each worker is an implementation.
In Dart, every class automatically has an interface made of its public members. There is no separate thing to declare.
2. Implementing a Class with implements
class B implements A means "a B can do everything an A can do":
class Printer {
void printPage(String text) {
print('Printing: $text');
}
}
class LaserPrinter implements Printer {
@override
void printPage(String text) {
print('Laser printing: $text');
}
}
void main() {
LaserPrinter().printPage('Report');
}Laser printing: ReportEach member of the interface is written with @override, exactly like an override from
Lecture 060.
3. No Code Is Inherited
extends copies the parent's code into the child. implements copies nothing — only the
promise. Leave a method out and Dart stops you:
class LaserPrinter implements Printer {}Error: The non-abstract class 'LaserPrinter' is missing implementations for these members:
- Printer.printPage
Try to either
- provide an implementation,
- inherit an implementation from a superclass or mixin,
- mark the class as abstract, or
- provide a 'noSuchMethod' implementation.For the same reason, super.printPage(text) does not work inside LaserPrinter: its
superclass is still Object, which has no printPage.
Error: Superclass has no method named 'printPage'.4. Fields Are Part of the Interface
A field gives a class a getter (and, if it is not final, a setter). Both belong to the
interface, so the implementing class must provide them:
class Person {
Person(this.name);
String name;
}
class Robot implements Person {
@override
String get name => 'R2';
}Error: The non-abstract class 'Robot' is missing implementations for these members:
- Person.nameA getter alone is not enough — name can also be assigned, so a setter is missing. The
simplest fix is a field of our own, which supplies both:
class Person {
Person(this.name);
String name;
}
class Robot implements Person {
Robot(this.name);
@override
String name;
}
void main() {
var robot = Robot('R2');
robot.name = 'R3';
print(robot.name);
}R3Notice that Robot writes its own constructor. Constructors are not part of an
interface, so Person's constructor is never called.
5. Implementing Several Interfaces
A class can implement as many interfaces as it needs, separated by commas. This is how the photocopier works:
class Printer {
void printPage(String text) {
print('Printing: $text');
}
}
class Scanner {
void scan() {
print('Scanning...');
}
}
class Photocopier implements Printer, Scanner {
@override
void printPage(String text) {
print('Copier prints: $text');
}
@override
void scan() {
print('Copier scans the page');
}
}
void main() {
var copier = Photocopier();
copier.scan();
copier.printPage('Invoice');
}Copier scans the page
Copier prints: Invoice6. An Abstract Class as a Pure Interface
Implementing a normal class throws its code away, which feels wasteful. The usual style is an abstract class with no code at all — only abstract members, as in Lecture 062:
abstract class Payable {
double get amount;
void pay();
}
class Bill implements Payable {
Bill(this.amount);
@override
final double amount;
@override
void pay() {
print('Paid bill of $amount');
}
}
void main() {
Bill(2500).pay();
}Paid bill of 2500.0Payable describes a role, and nothing is lost by implementing it. The abstract
double get amount needs only a getter, so a final field satisfies it.
Dart also has an interface class modifier that controls how a class may be used from other
files. We will study it in Lecture 130 — Class Modifiers.
7. The Object Has the Interface's Type
An object of Bill is a Payable. It passes is checks and fits any variable or
parameter of that type:
abstract class Payable {
double get amount;
}
class Bill implements Payable {
Bill(this.amount);
@override
final double amount;
}
void showAmount(Payable item) {
print('Amount due: ${item.amount}');
}
void main() {
Object thing = Bill(800);
print(thing is Payable);
showAmount(Bill(800));
}true
Amount due: 800.0showAmount accepts anything that is Payable, whatever its class. Using one type for many
classes is polymorphism — Lecture 065.
8. extends vs implements
extends | implements | |
|---|---|---|
| How many? | exactly one class | any number |
| Code inherited? | yes — fields and method bodies | no — you write every member |
super.method() | calls the parent's code | not available |
| Parent constructor | must be called | not involved |
| Meaning | "is a kind of, and works like" | "can do the same things as" |
Use extends to reuse code from a close relative. Use implements to promise
behaviour without taking anyone's code.
9. Combining extends and implements
One class can do both — extends comes first:
class Animal {
Animal(this.name);
final String name;
}
abstract class Swimmer {
void swim();
}
abstract class Flyer {
void fly();
}
class Duck extends Animal implements Swimmer, Flyer {
Duck(super.name);
@override
void swim() => print('$name paddles');
@override
void fly() => print('$name flies');
}
void main() {
var duck = Duck('Donald');
duck.swim();
duck.fly();
}Donald paddles
Donald fliesDuck inherits name from Animal and promises the Swimmer and Flyer roles. Writing
the clauses the other way round is an error:
Error: The extends clause must be before the implements clause.10. A Real-World Example
A shop where different items share the same roles:
abstract class Discountable {
double discountedPrice(double rate);
}
abstract class Printable {
String get label;
}
class Product {
Product(this.name, this.price);
final String name;
final double price;
}
class Book extends Product implements Discountable, Printable {
Book(super.name, super.price);
@override
double discountedPrice(double rate) => price - price * rate;
@override
String get label => 'Book: $name';
}
void main() {
var book = Book('Dart Basics', 1200);
print(book.label);
print('After 25% off: ${book.discountedPrice(0.25)}');
}Book: Dart Basics
After 25% off: 900.0Book reuses name and price from Product with extends, and promises two independent
roles with implements, writing each member itself.
Code Examples
Example 1 — Implementing a Class
class Greeter {
void greet() => print('Hello');
}
class LoudGreeter implements Greeter {
@override
void greet() => print('HELLO!');
}
void main() {
LoudGreeter().greet();
}HELLO!Example 2 — A Getter from the Interface
abstract class Named {
String get name;
}
class Student implements Named {
@override
String get name => 'Zayan';
}
void main() {
print(Student().name);
}ZayanExample 3 — A Field Satisfies Getter and Setter
class Counter {
int count = 0;
}
class StepCounter implements Counter {
@override
int count = 0;
}
void main() {
var steps = StepCounter();
steps.count = 5000;
print(steps.count);
}5000Example 4 — Two Interfaces
abstract class Reader {
void read();
}
abstract class Writer {
void write();
}
class Notebook implements Reader, Writer {
@override
void read() => print('Reading notes');
@override
void write() => print('Writing notes');
}
void main() {
var notebook = Notebook();
notebook.write();
notebook.read();
}Writing notes
Reading notesExample 5 — An is Check
abstract class Chargeable {
void charge();
}
class Phone implements Chargeable {
@override
void charge() => print('Phone charging');
}
void main() {
Object device = Phone();
print(device is Chargeable);
Phone().charge();
}true
Phone chargingExample 6 — extends and implements Together
class Vehicle {
void start() => print('Engine on');
}
abstract class Electric {
int get batteryLevel;
}
class ElectricCar extends Vehicle implements Electric {
@override
int get batteryLevel => 80;
}
void main() {
var car = ElectricCar();
car.start();
print('Battery: ${car.batteryLevel}%');
}Engine on
Battery: 80%start comes from Vehicle; batteryLevel had to be written.
Example 7 — Student Marks
abstract class Gradable {
int get marks;
String get grade;
}
class Student implements Gradable {
Student(this.name, this.marks);
final String name;
@override
final int marks;
@override
String get grade => marks >= 80 ? 'A' : 'B';
}
void main() {
var ali = Student('Ali', 85);
print('${ali.name}: ${ali.marks} (${ali.grade})');
}Ali: 85 (A)How it works
Gradablepromises two getters and contains no code.- The
finalfieldmarkssatisfiesint get marks. gradeis a calculated getter, written inStudentitself.
Example 8 — Shopping Total
abstract class Priced {
double get total;
}
class CartItem implements Priced {
CartItem(this.price, this.quantity);
final double price;
final int quantity;
@override
double get total => price * quantity;
}
void main() {
var item = CartItem(450, 3);
print('Total: ${item.total}');
}Total: 1350.0How it works
- Any
Pricedobject must be able to report atotal. CartItemdecides how: price times quantity.
Example 9 — Bank Balance
abstract class Account {
double get balance;
void deposit(double amount);
}
class SavingsAccount implements Account {
double _balance = 0;
@override
double get balance => _balance;
@override
void deposit(double amount) {
_balance += amount;
}
}
void main() {
var sara = SavingsAccount();
sara.deposit(1500);
sara.deposit(500);
print('Balance: ${sara.balance}');
}Balance: 2000.0How it works
- The interface asks for a
balancegetter only, so a private field plus a getter keeps the balance read-only from outside (Lecture 057). depositis the only way to change it.
Example 10 — Temperature Sensors
abstract class Sensor {
double read();
}
class IndoorSensor implements Sensor {
@override
double read() => 24.5;
}
class OutdoorSensor implements Sensor {
@override
double read() => 31.0;
}
void report(Sensor sensor) {
print('Temperature: ${sensor.read()}');
}
void main() {
report(IndoorSensor());
report(OutdoorSensor());
}Temperature: 24.5
Temperature: 31.0How it works
- Two unrelated classes implement the same interface.
reportaccepts anySensor, and each object runs its ownread.
Common Beginner Mistakes
1. Expecting implements to inherit code
class Printer {
void printPage(String text) => print(text);
}
// class LaserPrinter implements Printer {} // ✗ printPage is missingError: The non-abstract class 'LaserPrinter' is missing implementations for these members:
- Printer.printPageWrite every member yourself — or use extends if you really want the code.
2. Providing only a getter for a non-final field
class Person {
Person(this.name);
String name;
}
// class Robot implements Person {
// @override
// String get name => 'R2'; // ✗ the setter is still missing
// }Error: The non-abstract class 'Robot' is missing implementations for these members:
- Person.nameDeclare a field String name (or add a setter too).
3. Calling super in an implementing class
class LaserPrinter implements Printer {
@override
void printPage(String text) {
// super.printPage(text); // ✗ there is no parent code to call
print('Laser: $text');
}
}Error: Superclass has no method named 'printPage'.4. Using super parameters with implements
class Person {
Person(this.name);
final String name;
}
// class Robot implements Person {
// Robot(super.name); // ✗ Person's constructor is not involved
// }Error: The super constructor has no corresponding positional parameter.Use this.name and declare the field: Robot(this.name); @override final String name;.
5. Writing implements before extends
// class Duck implements Swimmer extends Animal {} // ✗Error: The extends clause must be before the implements clause.6. Trying to extend two classes
// class Photocopier extends Printer, Scanner {} // ✗Error: Each class definition can have at most one extends clause.Extend one at most, implement the rest.
Best Practices
- Describe roles with abstract classes that contain no code, then implement them.
- Use
extendsto reuse code,implementsto promise behaviour. - Write
@overrideon every member you implement. - Keep interfaces small — one role each (
Printable,Payable) — and combine them. - Name roles after what the object can do:
Chargeable,Discountable,Sensor. - Write parameter types as the interface (
void report(Sensor sensor)) so any implementation fits.
Mini Challenge
Create an abstract class Shape with a getter double get area. Write Square (side 4)
and Circle (radius 1, use 3.14) that implement it, and a function printArea(Shape s).
Your program should print exactly:
Area: 16.0
Area: 3.14Try it yourself before opening the solution.
Show solution
abstract class Shape {
double get area;
}
class Square implements Shape {
Square(this.side);
final double side;
@override
double get area => side * side;
}
class Circle implements Shape {
Circle(this.radius);
final double radius;
@override
double get area => 3.14 * radius * radius;
}
void printArea(Shape s) {
print('Area: ${s.area}');
}
void main() {
printArea(Square(4));
printArea(Circle(1));
}Area: 16.0
Area: 3.14How it works
Shapelists one getter and has no code.- Each class writes its own
area. printAreaaccepts both because both areShapes.
Practice Questions
Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.
Beginner
- Write a class
Bellwith a methodring()that printsRing. WriteDoorBell implements Bellwhosering()printsDing dong, and call it. - Create an abstract class
NamedwithString get name. Implement it inCity, returning'Lahore', and print the name. - In one sentence each: what does
extendsinherit, and what doesimplementsinherit?
Application
-
This class does not compile. Name the missing member and fix it:
class Pet { Pet(this.name); String name; } class RobotPet implements Pet {}Then create
RobotPet('Bolt')and print its name. -
Create abstract classes
Walker(void walk()) andTalker(void talk()). WriteChild implements Walker, Talker, with outputsZayan walksandZayan talks. -
Write an abstract class
Scoredwithint get score. Implement it inGamewith afinalfield, and printScore: 70forGame(70). -
Write a function
void describe(Scored s)that printsScore is 70and call it with theGamefrom Question 6.
Challenge
- Write
class Employeewith afinal String name, and an abstract classPayablewithdouble get salary. WriteManager extends Employee implements Payablewith a fixed salary of90000. PrintSara earns 90000.0. - Write an abstract class
Walletwithdouble get balanceandvoid spend(double amount). Implement it so spending more than the balance printsNot enough moneyand leaves the balance unchanged. Start at500, spend200, then400, and print the balance. - Write abstract classes
Heater(void heat()) andThermometer(double get temperature). WriteSmartKettle implements Heater, Thermometer: it starts at20.0and everyheat()adds40.0. Heat it twice and printTemperature: 100.0.
Practice Answers
Question 1 — Solution
class Bell {
void ring() => print('Ring');
}
class DoorBell implements Bell {
@override
void ring() => print('Ding dong');
}
void main() {
DoorBell().ring();
}Ding dongQuestion 2 — Solution
abstract class Named {
String get name;
}
class City implements Named {
@override
String get name => 'Lahore';
}
void main() {
print(City().name);
}LahoreQuestion 3 — Solution
extends inherits the parent's fields and method bodies. implements inherits no code — only
the obligation to provide every member of the interface.
Question 4 — Solution
Pet.name — both its getter and its setter — is missing. A field provides both:
class Pet {
Pet(this.name);
String name;
}
class RobotPet implements Pet {
RobotPet(this.name);
@override
String name;
}
void main() {
print(RobotPet('Bolt').name);
}BoltQuestion 5 — Solution
abstract class Walker {
void walk();
}
abstract class Talker {
void talk();
}
class Child implements Walker, Talker {
@override
void walk() => print('Zayan walks');
@override
void talk() => print('Zayan talks');
}
void main() {
var child = Child();
child.walk();
child.talk();
}Zayan walks
Zayan talksQuestion 6 — Solution
abstract class Scored {
int get score;
}
class Game implements Scored {
Game(this.score);
@override
final int score;
}
void main() {
print('Score: ${Game(70).score}');
}Score: 70Question 7 — Solution
Add this function to Question 6 and change main:
void describe(Scored s) {
print('Score is ${s.score}');
}
void main() {
describe(Game(70));
}Score is 70Question 8 — Solution
class Employee {
Employee(this.name);
final String name;
}
abstract class Payable {
double get salary;
}
class Manager extends Employee implements Payable {
Manager(super.name);
@override
double get salary => 90000;
}
void main() {
var sara = Manager('Sara');
print('${sara.name} earns ${sara.salary}');
}Sara earns 90000.0Question 9 — Solution
abstract class Wallet {
double get balance;
void spend(double amount);
}
class CashWallet implements Wallet {
CashWallet(this._balance);
double _balance;
@override
double get balance => _balance;
@override
void spend(double amount) {
if (amount > _balance) {
print('Not enough money');
return;
}
_balance -= amount;
}
}
void main() {
var wallet = CashWallet(500);
wallet.spend(200);
wallet.spend(400);
print('Balance: ${wallet.balance}');
}Not enough money
Balance: 300.0Question 10 — Solution
abstract class Heater {
void heat();
}
abstract class Thermometer {
double get temperature;
}
class SmartKettle implements Heater, Thermometer {
@override
double temperature = 20.0;
@override
void heat() {
temperature += 40.0;
}
}
void main() {
var kettle = SmartKettle();
kettle.heat();
kettle.heat();
print('Temperature: ${kettle.temperature}');
}Temperature: 100.0A field may implement a getter-only member; the extra setter does no harm.
Quick Revision
- What is an interface?
- Which Dart classes have an interface?
- What code does
implementsinherit? - Why must a non-
finalfield be implemented with both a getter and a setter? - How many classes can a class extend? How many can it implement?
- Why is an abstract class with no code the usual way to write an interface?
- In
class A extends B implements C, D, which class provides code?
Key Takeaways
- An interface is a class's public members — what it can do, not how.
- Every class has an implicit interface; no extra declaration is needed.
implementsinherits no code: every method, getter, and setter must be written.- A field in the interface means a getter (and a setter, unless
final) to provide. supercalls and the parent's constructor play no part inimplements.- A class can implement many interfaces but extend only one class.
- Abstract classes with no code are the natural way to describe a role.
class A extends B implements C, DreusesB's code and promisesCandD.
What's Next?
Lecture 64 — Mixins. implements lets a class take on many roles, but it must write
every member itself — even when ten classes need the same code. Next we will learn how mixins
share ready-made code across classes that have no common parent.