Dart 接口与 Mixin
虽然 Dart 只支持单继承,但通过接口和 Mixin 可以灵活地实现多重代码复用。
本章介绍如何使用 implements 实现接口、mixin 的代码复用以及 with 关键字。
implements 实现接口
在 Dart 中,每个类都隐式定义了一个接口。
使用 implements 关键字可以让一个类实现另一个类的接口,且可以实现多个。
实例
// 定义一个"可打印"的接口
class Printable {
// 接口中的方法没有默认实现
// 实现者必须重写所有成员
void printContent() {
// 这个方法体在 implements 时会被忽略
}
}
// 定义一个"可保存"的接口
class Savable {
void save() {}
}
// 使用 implements 实现多个接口
class Document implements Printable, Savable {
String title;
String content;
Document(this.title, this.content);
// 必须实现 Printable 接口要求的所有方法
@override
void printContent() {
print('--- 文档: $title ---');
print(content);
print('--- RUNOOB 打印结束 ---');
}
// 必须实现 Savable 接口要求的所有方法
@override
void save() {
print('文档 "$title" 已保存到磁盘');
}
}
void main() {
var doc = Document('Dart 教程', '这是一份 Dart 入门指南');
// 多态:Document 既是 Printable 也是 Savable
Printable printable = doc;
printable.printContent();
Savable savable = doc;
savable.save();
}
class Printable {
// 接口中的方法没有默认实现
// 实现者必须重写所有成员
void printContent() {
// 这个方法体在 implements 时会被忽略
}
}
// 定义一个"可保存"的接口
class Savable {
void save() {}
}
// 使用 implements 实现多个接口
class Document implements Printable, Savable {
String title;
String content;
Document(this.title, this.content);
// 必须实现 Printable 接口要求的所有方法
@override
void printContent() {
print('--- 文档: $title ---');
print(content);
print('--- RUNOOB 打印结束 ---');
}
// 必须实现 Savable 接口要求的所有方法
@override
void save() {
print('文档 "$title" 已保存到磁盘');
}
}
void main() {
var doc = Document('Dart 教程', '这是一份 Dart 入门指南');
// 多态:Document 既是 Printable 也是 Savable
Printable printable = doc;
printable.printContent();
Savable savable = doc;
savable.save();
}
--- 文档: Dart 教程 --- 这是一份 Dart 入门指南 --- RUNOOB 打印结束 --- 文档 "Dart 教程" 已保存到磁盘
implements 和 extends 的关键区别:extends 继承父类的所有实现(方法体),而 implements 只继承接口签名,你必须重新实现所有方法。此外,extends 只能继承一个类,implements 可以实现多个接口。
extends vs implements 对比
| 特性 | extends | implements |
|---|---|---|
| 数量限制 | 只能继承一个 | 可以实现多个 |
| 方法实现 | 继承父类的实现 | 必须自己实现所有方法 |
| 构造函数 | 可以调用 super() | 不继承构造函数 |
| 使用场景 | "是一个"的关系 | "能做什么"的契约 |
多接口实现
Dart 可以实现多个接口,这是突破单继承限制的主要方式。
实例
// 定义多个行为接口
class Flyable {
void fly() {}
}
class Swimmable {
void swim() {}
}
class Walkable {
void walk() {}
}
// 一只鸭子可以飞、游、走
class Duck implements Flyable, Swimmable, Walkable {
String name;
Duck(this.name);
@override
void fly() {
print('$name 在天空中飞翔');
}
@override
void swim() {
print('$name 在水面上游泳');
}
@override
void walk() {
print('$name 在陆地上摇摇晃晃地走');
}
}
// 一条鱼只能游
class Fish implements Swimmable {
@override
void swim() {
print('鱼儿在水中游动');
}
}
void main() {
var duck = Duck('RUNOOB 鸭');
// 鸭子可以扮演多种角色
(duck as Flyable).fly();
(duck as Swimmable).swim();
(duck as Walkable).walk();
// 类型检查
print('鸭子能飞吗? ${duck is Flyable}');
print('鸭子能游吗? ${duck is Swimmable}');
var fish = Fish();
print('鱼能飞吗? ${fish is Flyable}');
}
class Flyable {
void fly() {}
}
class Swimmable {
void swim() {}
}
class Walkable {
void walk() {}
}
// 一只鸭子可以飞、游、走
class Duck implements Flyable, Swimmable, Walkable {
String name;
Duck(this.name);
@override
void fly() {
print('$name 在天空中飞翔');
}
@override
void swim() {
print('$name 在水面上游泳');
}
@override
void walk() {
print('$name 在陆地上摇摇晃晃地走');
}
}
// 一条鱼只能游
class Fish implements Swimmable {
@override
void swim() {
print('鱼儿在水中游动');
}
}
void main() {
var duck = Duck('RUNOOB 鸭');
// 鸭子可以扮演多种角色
(duck as Flyable).fly();
(duck as Swimmable).swim();
(duck as Walkable).walk();
// 类型检查
print('鸭子能飞吗? ${duck is Flyable}');
print('鸭子能游吗? ${duck is Swimmable}');
var fish = Fish();
print('鱼能飞吗? ${fish is Flyable}');
}
RUNOOB 鸭 在天空中飞翔 RUNOOB 鸭 在水面上游泳 RUNOOB 鸭 在陆地上摇摇晃晃地走 鸭子能飞吗? true 鸭子能游吗? true 鱼能飞吗? false
Mixin 代码复用
Mixin 是一种在多个类之间复用代码的机制,它解决了"多个类需要共享相同方法但又不适合继承"的问题。
使用 mixin 关键字定义一个 Mixin,使用 with 关键字将其混入到类中。
实例
// 使用 mixin 关键字定义
mixin Logger {
// Mixin 中的方法可以被多个类复用
void log(String message) {
print('[${DateTime.now()}] $message');
}
void logError(String message) {
print('[ERROR ${DateTime.now()}] $message');
}
}
mixin TimestampFormatter {
String formatTimestamp(DateTime dt) {
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}'
'-${dt.day.toString().padLeft(2, '0')}';
}
}
// 使用 with 关键字混入 Mixin
class UserService with Logger, TimestampFormatter {
void createUser(String name) {
log('创建用户: $name');
var now = DateTime.now();
print('创建时间: ${formatTimestamp(now)}');
}
}
class OrderService with Logger {
void createOrder(String orderId) {
log('创建订单: $orderId');
}
void cancelOrder(String orderId) {
logError('取消订单失败: $orderId');
}
}
void main() {
var userService = UserService();
userService.createUser('runoob');
var orderService = OrderService();
orderService.createOrder('ORD-001');
orderService.cancelOrder('ORD-001');
}
mixin Logger {
// Mixin 中的方法可以被多个类复用
void log(String message) {
print('[${DateTime.now()}] $message');
}
void logError(String message) {
print('[ERROR ${DateTime.now()}] $message');
}
}
mixin TimestampFormatter {
String formatTimestamp(DateTime dt) {
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}'
'-${dt.day.toString().padLeft(2, '0')}';
}
}
// 使用 with 关键字混入 Mixin
class UserService with Logger, TimestampFormatter {
void createUser(String name) {
log('创建用户: $name');
var now = DateTime.now();
print('创建时间: ${formatTimestamp(now)}');
}
}
class OrderService with Logger {
void createOrder(String orderId) {
log('创建订单: $orderId');
}
void cancelOrder(String orderId) {
logError('取消订单失败: $orderId');
}
}
void main() {
var userService = UserService();
userService.createUser('runoob');
var orderService = OrderService();
orderService.createOrder('ORD-001');
orderService.cancelOrder('ORD-001');
}
[2026-06-12 10:30:00.000] 创建用户: runoob 创建时间: 2026-06-12 [2026-06-12 10:30:00.001] 创建订单: ORD-001 [ERROR 2026-06-12 10:30:00.001] 取消订单失败: ORD-001
UserService 和 OrderService 来自不同的业务领域,不适合用继承来共享 Logger。
通过 Mixin,它们都能获得日志功能,而且互不影响。
Mixin 不能有构造函数,也不能被实例化。它的定位很纯粹:提供可复用的方法,不涉及状态初始化。
Mixin 的限制条件
Mixin 可以使用 on 关键字限制它只能被特定类型的类使用。
实例
// 基础类
class Animal {
String name;
Animal(this.name);
}
// 这个 Mixin 只能混入到 Animal 及其子类中
mixin FlyableMixin on Animal {
void fly() {
print('$name 飞起来了!');
}
}
// Bird 是 Animal,可以用 FlyableMixin
class Bird extends Animal with FlyableMixin {
Bird(String name) : super(name);
}
// 下面这行会报错:Car 不是 Animal,不能用 FlyableMixin
// class Car with FlyableMixin {} // 错误!
void main() {
var bird = Bird('RUNOOB 小鸟');
bird.fly();
// FlyableMixin 中的方法可以访问 Animal 的属性 name
print('小鸟的名字: ${bird.name}');
}
class Animal {
String name;
Animal(this.name);
}
// 这个 Mixin 只能混入到 Animal 及其子类中
mixin FlyableMixin on Animal {
void fly() {
print('$name 飞起来了!');
}
}
// Bird 是 Animal,可以用 FlyableMixin
class Bird extends Animal with FlyableMixin {
Bird(String name) : super(name);
}
// 下面这行会报错:Car 不是 Animal,不能用 FlyableMixin
// class Car with FlyableMixin {} // 错误!
void main() {
var bird = Bird('RUNOOB 小鸟');
bird.fly();
// FlyableMixin 中的方法可以访问 Animal 的属性 name
print('小鸟的名字: ${bird.name}');
}
RUNOOB 小鸟 飞起来了! 小鸟的名字: RUNOOB 小鸟
class、Mixin、extends、implements 的完整组合
Dart 中一个类可以同时使用 extends、with 和 implements:
实例
mixin Jumpable {
void jump() => print('跳起来!');
}
mixin Runnable {
void run() => print('跑步前进!');
}
class Animal {
void breathe() => print('呼吸...');
}
// 继承 Animal,混入 Jumpable 和 Runnable,实现 Comparable
class Athlete extends Animal
with Jumpable, Runnable
implements Comparable<Athlete> {
String name;
int score;
Athlete(this.name, this.score);
@override
int compareTo(Athlete other) => score.compareTo(other.score);
void showSkills() {
breathe();
run();
jump();
}
}
void main() {
var a1 = Athlete('RUNOOB 选手A', 95);
var a2 = Athlete('选手B', 88);
a1.showSkills();
print('${a1.name} vs ${a2.name}: ${a1.compareTo(a2) > 0 ? "A 胜" : "B 胜"}');
}
void jump() => print('跳起来!');
}
mixin Runnable {
void run() => print('跑步前进!');
}
class Animal {
void breathe() => print('呼吸...');
}
// 继承 Animal,混入 Jumpable 和 Runnable,实现 Comparable
class Athlete extends Animal
with Jumpable, Runnable
implements Comparable<Athlete> {
String name;
int score;
Athlete(this.name, this.score);
@override
int compareTo(Athlete other) => score.compareTo(other.score);
void showSkills() {
breathe();
run();
jump();
}
}
void main() {
var a1 = Athlete('RUNOOB 选手A', 95);
var a2 = Athlete('选手B', 88);
a1.showSkills();
print('${a1.name} vs ${a2.name}: ${a1.compareTo(a2) > 0 ? "A 胜" : "B 胜"}');
}
呼吸... 跑步前进! 跳起来! RUNOOB 选手A vs 选手B: A 胜
完整声明顺序:class 子类 extends 父类 with Mixin1, Mixin2 implements 接口1, 接口2
