Implicit Interfaces
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 46 of 110.
Since every Dart class automatically defines an interface, you can use any class as an interface - even concrete classes with full implementations. This is what we call an implicit interface.
Consider a class with properties and methods:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}When another class implements Logger, it must provide its own implementation for everything - including the prefix property:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}This is the key insight: when you use implements, you're saying "my class will have the same shape" - the same methods and properties. You don't inherit any code, constructors, or default values. You're only promising to match the interface's structure.
This makes implicit interfaces powerful for creating interchangeable components. Code that expects a Logger can work with any class that implements its interface, regardless of how that implementation works internally.
Challenge
EasyLet's build a notification system that demonstrates how concrete classes can serve as implicit interfaces. You'll create a base notification class and then implement it in a completely different way, showing that implements requires you to provide all members yourself.
You'll organize your code into two files:
notification.dart: Define your notification classes here:- A
Notificationclass with aString channelproperty set to'default', a methodsend(String message)that prints[channel]: [message], and a methodgetStatus()that returns the string'Notification ready' - An
EmailNotificationclass that implementsNotification. Since you're usingimplements, you must provide your ownchannelproperty (set it to'email'), your ownsend()method that printsSending email: [message], and your owngetStatus()method that returns'Email service active' - A
PushNotificationclass that implementsNotification. Set itschannelto'push', makesend()printPush alert: [message], and havegetStatus()return'Push service connected'
- A
main.dart: Import your notification file and demonstrate how different classes can implement the same implicit interface with completely different behaviors:- Create an
EmailNotificationand aPushNotification - For each notification, print its status using
getStatus(), then callsend()with the message'Hello World' - Print an empty line between the two notifications
- Create an
Remember: when you use implements, you're not inheriting any code from Notification. You're only promising that your class will have the same shape - the same properties and methods. Each implementing class provides its own complete implementation.
Expected output:
Email service active
Sending email: Hello World
Push service connected
Push alert: Hello WorldCheat sheet
In Dart, every class automatically defines an implicit interface. This means you can use any class as an interface, even concrete classes with full implementations.
When a class uses implements, it must provide its own implementation for all properties and methods from the interface. You don't inherit any code, constructors, or default values - you only promise to match the interface's structure.
Example of a concrete class:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Implementing the implicit interface:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}This makes implicit interfaces powerful for creating interchangeable components. Code that expects a Logger can work with any class that implements its interface, regardless of internal implementation.
Try it yourself
import 'notification.dart';
void main() {
// TODO: Create an EmailNotification instance
// TODO: Print the email notification's status using getStatus()
// TODO: Send a message 'Hello World' using the email notification
// TODO: Print an empty line
// TODO: Create a PushNotification instance
// TODO: Print the push notification's status using getStatus()
// TODO: Send a message 'Hello World' using the push notification
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesLibraries & ImportsIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsInstance VariablesConstructor BasicsRecap - Simple Calculator4Null Safety
Intro to Null SafetyNullable vs Non-NullableThe ? and ! OperatorsLate Keyword & Null SafetyNull-Aware OperatorsNull Safety in ClassesRecap - User Profile System7Abstract Classes & Interfaces
Abstract ClassesAbstract MethodsInterfaces in DartImplicit InterfacesImplementing vs ExtendingMultiple InterfacesRecap - Shape Calculator10Collections & Generics
List, Set, Map OverviewType-Safe CollectionsGeneric ClassesGeneric MethodsGeneric ConstraintsIterable & IteratorRecap - Generic Storage13Advanced OOP Concepts
Composition vs InheritanceExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums with Methods16Project: Library Management
Project OverviewBook and User Classes2Constructors in Dart
Default ConstructorNamed ConstructorsInitializer ListsConstant ConstructorsFactory ConstructorsRedirecting ConstructorsRecap - Shape Builder5Encapsulation
Public vs Private MembersThe _ Prefix ConventionLibrary-Level PrivacyGetters & Setters DepthInformation HidingRecap - Student Records8Mixins
Introduction to MixinsCreating MixinsUsing Multiple Mixinson Keyword in MixinsMixin vs InheritanceMixin vs InterfaceRecap - Animal System11Special Methods
toString() OverridehashCode & == OverrideComparable Interfacecall() MethodnoSuchMethod OverrideRecap - Custom Collection14Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern3Class Properties
Instance vs Static MembersFinal & Const FieldsLate VariablesStatic Methods & FieldsGetters and SettersRecap - Bank Account Manager6Inheritance
Basic InheritanceThe super KeywordMethod OverridingThe @override AnnotationThe final Class KeywordConstructors & InheritanceRecap - Employee Hierarchy9Polymorphism
Polymorphism BasicsPolymorphism via InterfacesRuntime Type CheckingThe is & as OperatorsCovariant KeywordRecap - Payment Processor