DART / CHAPTER 09
Interfaces and substitution
AdvancedImplement a contract without inheriting implementation accidentally.
By the end of this chapter
- Use an abstract class contract
- Distinguish implements from extends
- Design a uniform client
The idea
Every Dart class defines an interface for its public instance members. implements promises to provide that interface, but does not inherit the class’s method bodies. extends instead establishes class inheritance and reuses compatible implementation. An abstract class can declare operations that concrete classes must supply. Choose the relationship according to whether you need a contract, implementation reuse, or both.
A client that accepts an interface can work with several unrelated implementations. The method signatures tell the compiler what calls are available, while the actual object supplies the behavior. Agree on semantic details such as units, allowed inputs, and side effects. An implementation that silently changes those rules can break callers despite satisfying the compiler.
abstract class Cost { int cents(); }
class FixedCost implements Cost {
@override int cents() => 250;
}
class VariableCost implements Cost {
@override int cents() => 400;
}
void main() {
final costs = <Cost>[FixedCost(), VariableCost()];
print(costs.fold<int>(0, (sum, cost) => sum + cost.cents()));
}650
Both concrete classes provide the required operation. The client sums results through the Cost type without inspecting implementation names.
Try it yourself
Define a LabelSource contract and implement it with two different sources of label text.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING