Dart之消息、继承、多态
Dart之消息、继承、多态

一、前言
上一篇面向对象基础里,我们已经知道:类像设计图,对象是具体实例。
但对象不是孤零零存在的。真正的程序里,对象之间要协作:一个对象请求另一个对象做事,子类复用父类能力,不同对象对同一个行为做出不同反应。
这就引出了三个重要概念:消息、继承、多态。
可以先用一句生活化的话理解:
- 消息是对象之间沟通的桥梁。
- 继承是已有能力的传递。
- 多态是同一个动作在不同对象上呈现不同效果。
二、一句话理解
消息让对象之间发生协作,继承让子类复用父类能力,多态让同一个调用在不同对象上产生不同表现。
简单说:
- 消息:对象调用另一个对象的方法。
- 继承:子类获得父类的非私有属性和方法。
- 多态:父类类型接收子类对象,调用时表现出子类行为。
三、消息
3.1 消息是什么
消息是对象之间通信的一种方式。
在代码里,一个对象调用另一个对象的方法,本质上就可以理解为发送消息。
消息通常包含四个部分:
| 组成 | 含义 |
|---|---|
| 发送者 | 发起请求的对象 |
| 接收者 | 接收请求的对象 |
| 操作 | 要执行的方法 |
| 参数 | 执行方法需要的数据 |
例如:
receiver.receiveMessage('Hello, Dart!');
这里可以理解为:给 receiver 发送一条消息,请它执行 receiveMessage,参数是 'Hello, Dart!'。
3.2 消息传递
消息传递是对象协作的过程。
class Receiver {
void receiveMessage(String message) {
print(message);
}
}
class Sender {
void sendMessage(Receiver receiver, String message) {
receiver.receiveMessage(message);
}
}
void main() {
Receiver receiver = Receiver();
Sender sender = Sender();
sender.sendMessage(receiver, 'Hello, Dart!');
}
这个例子中:
sender是发送者。receiver是接收者。receiveMessage是接收者要执行的操作。'Hello, Dart!'是消息参数。
对象之间不是直接访问彼此内部细节,而是通过方法进行协作。
3.3 消息传递的优势
消息传递的价值不只是“能调用方法”,它背后有几个重要优势:
- 解耦:发送者不需要知道接收者内部怎么实现。
- 灵活:可以根据不同对象调用不同方法。
- 可扩展:新增能力时,可以扩展对象方法。
- 复用:相同协作方式可以复用。
- 支持多态:同一个消息可以在不同对象上表现不同。
消息传递让对象之间有边界。边界清楚,代码就更容易维护。
四、继承
4.1 继承是什么
继承是子类和父类之间共享属性和方法的机制。
可以把它理解成能力传递:父类提供通用能力,子类在此基础上扩展或改写。
class Animal {
String name;
Animal(this.name);
void roar() {
print('The $name is roaring');
}
}
class Dog extends Animal {
Dog(super.name);
}
void main() {
Dog dog = Dog('dog');
dog.roar();
}
Dog 继承了 Animal,所以拥有 name 和 roar()。
4.2 继承的优势
继承的优势主要有:
- 代码复用:公共属性和方法放在父类中。
- 扩展能力:子类可以新增自己的属性和方法。
- 结构清晰:类之间形成层次关系。
- 支持多态:父类引用可以指向子类对象。
例如:
class Dog extends Animal {
Dog(super.name);
void run() {
print('$name is running');
}
}
Dog 既有 Animal 的能力,又有自己的 run()。
4.3 方法重写
子类可以重写父类方法,定制自己的行为。
class Dog extends Animal {
Dog(super.name);
@override
void roar() {
print('$name is running!');
}
}
void main() {
Animal animal = Animal('animal');
Dog dog = Dog('dog');
animal.roar();
dog.roar();
}
@override 表示这个方法重写了父类方法。它能让代码意图更清楚,也能帮助工具检查错误。
4.4 继承相关概念
几个术语要分清:
| 概念 | 含义 |
|---|---|
| 基类 / 父类 | 被继承的类 |
| 派生类 / 子类 | 继承而来的类 |
| 继承层次 | 多个类形成的树状结构 |
| 重写 | 子类重新定义父类已有方法 |
super |
在子类中访问父类成员 |
super 常用于调用父类构造函数或父类方法。
class Dog extends Animal {
Dog(super.name);
@override
void roar() {
super.roar();
print('$name says wang wang');
}
}
4.5 继承的边界
继承不是越多越好。
继承适合表达 “is-a” 关系:
- Dog is an Animal.
- Cat is an Animal.
如果只是一个对象“拥有”另一个对象,更适合组合。
例如汽车拥有发动机,不应该说汽车是发动机。
五、多态
5.1 多态是什么
多态就是同一个接口或同一个方法调用,在不同对象上表现出不同结果。
可以想象一个打印按钮:传入黑白打印机,打印黑白;传入彩色打印机,打印彩色。按钮动作一样,但对象不同,效果不同。
5.2 运行时多态
Dart 中最常见的是运行时多态,也就是子类重写父类方法。
class Animal {
String name;
Animal(this.name);
void roar() {
print('$name makes sound');
}
}
class Dog extends Animal {
Dog(super.name);
@override
void roar() {
print('$name 汪汪');
}
}
class Cat extends Animal {
Cat(super.name);
@override
void roar() {
print('$name 喵喵');
}
}
void main() {
List<Animal> animals = [
Dog('小狗'),
Cat('小猫'),
];
for (final animal in animals) {
animal.roar();
}
}
变量列表的类型是 List<Animal>,但里面放的是 Dog 和 Cat。调用同一个 roar(),输出不同结果。
这就是多态的价值。
5.3 编译时多态要注意
原文中提到编译时多态,也就是方法重载。
但 Dart 和 Java 这类语言不同,Dart 不支持在同一个类中通过不同参数列表定义多个同名方法。
例如下面这种写法在 Dart 中不成立:
class Calculator {
// Dart 不支持这样写多个同名 add 方法
// int add(int a, int b) => a + b;
// double add(double a, double b) => a + b;
}
Dart 更常用的方式是使用可选参数、命名参数、泛型或不同方法名来表达不同调用形式。
class Calculator {
num add(num a, num b) {
return a + b;
}
}
这一点需要特别注意,不能把其他语言里的重载机制直接套到 Dart 上。
5.4 多态的好处
多态的好处是让调用方依赖抽象,而不是依赖具体类。
void makeAnimalSound(Animal animal) {
animal.roar();
}
void main() {
makeAnimalSound(Dog('小狗'));
makeAnimalSound(Cat('小猫'));
}
makeAnimalSound 不关心传进来的是狗还是猫,只关心它是 Animal,能执行 roar()。
这样代码会更灵活,也更容易扩展。
六、消息、继承、多态之间的关系
这三个概念不是孤立的。
- 消息让对象之间能协作。
- 继承让类之间能复用和扩展。
- 多态让同一个消息在不同子类对象上表现不同。
例如:
void makeAnimalSound(Animal animal) {
animal.roar();
}
这行代码里:
animal.roar()是消息传递。Dog、Cat继承自Animal。- 运行时根据真实对象调用不同
roar(),这是多态。
把它们放在一起理解,会比单独背概念更清楚。
七、常见误区
7.1 以为继承只是复制代码
继承不是简单复制父类代码,而是表达类之间的层次关系。
如果没有 “is-a” 关系,优先考虑组合。
7.2 忽略 Dart 不支持传统方法重载
Dart 不支持同类中多个同名不同参数的方法重载。需要用可选参数、命名参数、泛型或不同方法名解决。
7.3 以为多态必须写很多 if
多态的价值恰恰是减少大量类型判断。
for (final animal in animals) {
animal.roar();
}
调用方不需要判断 animal 是狗还是猫,对象自己知道怎么表现。
7.4 子类重写方法忘记 @override
@override 不是强制必须,但建议写。
它能表达意图,也能帮助工具发现方法名写错、签名不一致等问题。
八、总结
这一篇我们系统讲了 Dart 中的消息、继承和多态。
消息是对象之间的沟通方式,本质上就是对象之间通过方法调用协作。
继承让子类复用父类的非私有属性和方法,也能在父类基础上扩展和重写。
多态让同一个方法调用在不同对象上有不同表现,是面向对象灵活性的关键。
最后记住一句话:
对象通过消息协作,类通过继承复用,行为通过多态扩展。
浙公网安备 33010602011771号