Dart 类与面向对象
1. 类的基本写法
类 = 属性(数据)+ 构造函数 + 方法(行为)
class Person {
// ========== 属性(Fields)==========
String name; // 姓名
int age; // 年龄
// ========== 构造函数 ==========
Person(this.name, this.age);
// ========== 方法(Functions)==========
void introduce() {
print('我叫 name,今年age 岁');
}
bool isAdult() {
return age >= 18;
}
}
// 使用类
void main() {
var person = Person('张三', 25);
person.introduce(); // 我叫 张三,今年 25 岁
print(person.isAdult()); // true
}
2. this 关键字详解
this 的核心作用:指向当前对象
可以把 this 理解为"我自己",它指向调用该方法的对象。
场景一:this 用于区分属性和局部变量
class Dog {
String name; // 属性
int age; // 属性
// 参数名和属性名相同,需要用 this 区分
Dog(this.name, this.age);
void speak() {
String name = '局部变量'; // 局部变量
print('我叫 {this.name}'); // this.name 指向属性
print('我叫name'); // 指向局部变量
}
}
void main() {
var dog = Dog('旺财', 3);
dog.speak();
// 输出:
// 我叫 旺财
// 我叫 局部变量
}
场景二:this 用于构造函数参数传值
class Person {
String name;
int age;
String city;
// 语法糖:this.属性名 直接接收参数并赋值给属性
Person(this.name, this.age); // city 没传,默认为 null
// 等价于:
// Person(String name, int age) {
// this.name = name;
// this.age = age;
// }
}
场景三:this 用于方法链式调用
class Builder {
String name = '';
int age = 0;
Builder setName(String name) {
this.name = name;
return this; // 返回自己,实现链式调用
}
Builder setAge(int age) {
this.age = age;
return this;
}
}
void main() {
var builder = Builder()
..setName('张三') // 级联运算符
..setAge(25);
// 等价于链式调用:
var builder2 = Builder()
.setName('李四')
.setAge(30);
}
场景四:继承中的 this
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
int age;
// super 用于调用父类构造函数
// this 用于初始化当前类的属性
Dog(String name, this.age) : super(name);
}
void main() {
var dog = Dog('旺财', 3);
print(dog.name); // 旺财
print(dog.age); // 3
}
this 快速记忆
| 场景 | 作用 |
|---|---|
this.属性名 |
区分同名属性和局部变量 |
this.name 在构造函数参数 |
语法糖,自动赋值属性 |
return this |
返回当前对象,实现链式调用 |
super.name |
调用父类构造函数 |
3. 类的继承
基本语法
// 父类
class Animal {
String name;
Animal(this.name);
void eat() {
print('name 正在吃东西');
}
}
// 子类 - 使用 extends 继承
class Dog extends Animal {
String breed; // 新增属性
Dog(String name, this.breed) : super(name); // super 调用父类构造函数
void bark() {
print('name 汪汪叫');
}
}
void main() {
var dog = Dog('旺财', '金毛');
dog.eat(); // 继承自父类的方法
dog.bark(); // 子类自己的方法
}
继承的规则
- 单继承:Dart 只支持单继承,一个类只能继承一个父类
- 继承属性和方法:子类自动获得父类的属性和方法
- 可新增或重写:子类可以添加新属性/方法,也可以重写父类方法
4. 方法重写(Override)
什么是方法重写?
子类重新定义父类中已有的方法,叫方法重写。
重写规则
- 使用
@override注解(建议添加,代码更清晰) - 方法签名(参数列表)必须与父类一致
- 方法体可以自定义
示例
class Animal {
String name;
Animal(this.name);
void sound() {
print('name 发出声音');
}
}
class Cat extends Animal {
Cat(String name) : super(name);
@override
void sound() { // 重写父类的 sound 方法
print('name 喵喵叫');
}
void climb() {
print('name 在爬树');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void sound() { // 重写父类的 sound 方法
print('name 汪汪叫');
}
}
void main() {
var cat = Cat('咪咪');
var dog = Dog('旺财');
cat.sound(); // 咪咪 喵喵叫(调用重写后的方法)
dog.sound(); // 旺财 汪汪叫(调用重写后的方法)
}
重写 vs 重载(区别)
| 概念 | 说明 | Dart 支持 |
|---|---|---|
| 重写(Override) | 子类重新定义父类方法 | ✅ 支持 |
| 重载(Overload) | 同名方法不同参数 | ❌ 不支持 |
5. 泛型(Generics)详解
为什么要用泛型?
问题:如果有多种类型需要存储,每种都写一个类太麻烦了
// ❌ 没有泛型 - 需要写多个类
class IntBox {
int? value;
}
class StringBox {
String? value;
}
class DoubleBox {
double? value;
}
// ✅ 有泛型 - 一个类搞定所有类型
class Box<T> { // T 是类型参数,可以是任何类型
T? value;
}
void main() {
var intBox = Box<int>();
var stringBox = Box<String>();
var doubleBox = Box<double>();
intBox.value = 42;
stringBox.value = 'Hello';
doubleBox.value = 3.14;
print(intBox.value); // 42
print(stringBox.value); // Hello
print(doubleBox.value); // 3.14
}
泛型的本质
泛型 = "类型参数"
用一个占位符(T)代替具体类型,使用时再指定具体类型
泛型语法
基本语法
class 类名<类型参数> {
类型参数 属性名;
}
// 常见类型参数名:T、E、K、V、R(只是一种约定)
常见命名约定
| 命名 | 含义 | 示例 |
|---|---|---|
T |
Type(类型) | List<T> |
E |
Element(元素) | Set<E> |
K |
Key(键) | Map<K, V> |
V |
Value(值) | Map<K, V> |
R |
Return(返回值) | R Function() |
泛型用法示例
泛型类
class Stack<T> {
final List<T> _items = [];
void push(T item) => _items.add(item);
T pop() => _items.removeLast();
}
// 使用
var intStack = Stack<int>();
var stringStack = Stack<String>();
泛型方法
T first<T>(List<T> list) {
return list[0];
}
// 使用
first<int>([1, 2, 3]); // 返回 1
first<String>(['a', 'b']); // 返回 'a'
泛型接口
abstract class Repository<T> {
Future<T> get(int id);
Future<List<T>> getAll();
}
class UserRepository implements Repository<User> {
@override
Future<User> get(int id) async {
// 实现
}
@override
Future<List<User>> getAll() async {
// 实现
}
}
多类型参数
class Pair<K, V> {
K first;
V second;
Pair(this.first, this.second);
}
// 使用
var pair = Pair<String, int>('年龄', 25);
泛型约束
限制泛型必须是某个类型的子类:
class Printable<T extends Comparable> {
void print(T value) {
print(value.toString());
}
}
// 使用
var p = Printable<int>(); // ✅ int 实现了 Comparable
// var p = Printable<String>(); // ❌ String 也实现了 Comparable,所以也可以
泛型的好处
| 好处 | 说明 |
|---|---|
| 代码复用 | 一个类可以处理多种类型 |
| 类型安全 | 编译时检查类型错误 |
| 性能优化 | 不需要运行时类型检查 |
6. 综合示例:完整类结构
// 泛型父类
abstract class Storage<T> {
void save(T item);
T? get(int id);
List<T> getAll();
}
// 子类继承 + 重写 + 泛型
class UserStorage implements Storage<User> {
final List<User> _users = [];
@override
void save(User user) {
_users.add(user);
}
@override
User? get(int id) {
return id < _users.length ? _users[id] : null;
}
@override
List<User> getAll() {
return List.from(_users);
}
}
class User {
String name;
int age;
User(this.name, this.age);
@override
String toString() => 'User(name,age)';
}
void main() {
var storage = UserStorage();
storage.save(User('张三', 25));
storage.save(User('李四', 30));
print(storage.get(0)); // User(张三, 25)
print(storage.getAll()); // [User(张三, 25), User(李四, 30)]
}
7. 快速记忆
🎯
this的作用:指向当前对象,区分属性与局部变量,实现链式调用🎯 继承:
extends继承父类,super()调用父类构造函数🎯 方法重写:
@override注解 + 签名一致 + 自定义方法体🎯 泛型:用
<T>表示"类型参数",<T>写在使用时替换为具体类型
8. abstract(抽象类)
什么是抽象类?
用 abstract 修饰的类不能直接实例化,只能被继承或实现。
为什么需要抽象类?
问题:有时候我们需要一个"模板"或"规范",规定子类必须有什么方法,但不关心具体怎么实现。
// ❌ 普通类:方法必须实现
class Animal {
void sound() {
// 父类不知道具体怎么叫,不知道子类想怎么叫
}
}
// ✅ 抽象类:定义规范,子类必须实现
abstract class Animal {
String name;
Animal(this.name);
// 抽象方法:只有声明,没有方法体
// 子类必须重写这个方法
void sound();
}
抽象类 vs 普通类
| 特性 | 抽象类 | 普通类 |
|---|---|---|
| 实例化 | ❌ 不能直接创建对象 | ✅ 可以 |
| 抽象方法 | 可以有,子类必须实现 | 不能有 |
| 普通方法 | 可以有,子类直接继承使用 | 必须有实现 |
| 使用场景 | 定义"规范/模板" | 直接创建对象使用 |
示例对比
// 抽象类 - 定义规范
abstract class Shape {
// 抽象方法 - 子类必须实现
double area();
// 普通方法 - 子类可以直接使用
void printInfo() {
print('面积: ${area()}');
}
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double area() {
return 3.14 * radius * radius;
}
}
class Square extends Shape {
double side;
Square(this.side);
@override
double area() {
return side * side;
}
}
void main() {
var circle = Circle(5);
var square = Square(4);
circle.printInfo(); // 面积: 78.5
square.printInfo(); // 面积: 16
// var shape = Shape(); // ❌ 错误!不能实例化抽象类
}
9. implements(实现接口)
什么是 implements?
implements 用于实现一个抽象类或接口,强制当前类提供某些方法的具体实现。
extends vs implements 对比
| 关键字 | 含义 | 特点 |
|---|---|---|
extends |
继承 | 获得父类功能,可选重写,可用 super |
implements |
实现 | 必须重写所有抽象方法 |
用 extends 继承
class Animal {
void eat() => print('吃东西');
}
class Dog extends Animal {
void bark() => print('汪汪叫');
}
void main() {
var dog = Dog();
dog.eat(); // 继承来的方法
dog.bark(); // 子类自己的方法
// Dog 自动有 eat 方法,可以直接用
}
用 implements 实现
abstract class Flyable {
void fly();
}
class Duck implements Flyable {
@override
void fly() {
print('鸭子扑腾翅膀');
}
}
class Plane implements Flyable {
@override
void fly() {
print('飞机起飞');
}
}
void main() {
var duck = Duck();
var plane = Plane();
duck.fly(); // 鸭子扑腾翅膀
plane.fly(); // 飞机起飞
// 必须重写 fly 方法,否则编译错误
}
核心区别:继承 vs 实现
// ========== extends(继承父类)==========
class Parent {
void greet() => print('你好');
}
class Child extends Parent {
@override
void greet() => print('嗨');
}
void main() {
var child = Child();
child.greet(); // 嗨
// Child 自动有 greet 方法,可以直接使用
// 可以选择重写,也可以不重写
}
// ========== implements(实现接口)==========
abstract class Greeter {
void greet(); // 抽象方法,没有实现
}
class FriendlyGreeter implements Greeter {
@override
void greet() => print('你好呀!');
}
class CasualGreeter implements Greeter {
@override
void greet() => print('嗨~');
}
void main() {
var friendly = FriendlyGreeter();
var casual = CasualGreeter();
friendly.greet(); // 你好呀!
casual.greet(); // 嗨~
// 必须重写 greet 方法,不能省略
}
多重实现
一个类可以实现多个接口:
abstract class Printer {
void print();
}
abstract class Scanner {
void scan();
}
class AllInOnePrinter implements Printer, Scanner {
@override
void print() => print('打印中...');
@override
void scan() => print('扫描中...');
}
10. 为什么泛型前面加 abstract?
abstract class Storage<T> {
void save(T item);
T? get(int id);
List<T> getAll();
}
原因
- 不能直接实例化:
Storage<int>()是错误的 - 定义规范:
Storage<T>定义了任何存储类必须有的方法 - 强制实现:任何
implements Storage<T>的类都必须实现这三个方法
使用场景
当你需要定义一个"规范",让多个类都必须实现某些方法时:
abstract class Repository<T> {
Future<T?> getById(int id);
Future<List<T>> getAll();
Future<void> save(T item);
Future<void> delete(int id);
}
// 实现接口
class UserRepository implements Repository<User> {
// 必须实现所有 4 个方法
@override
Future<User?> getById(int id) async { ... }
@override
Future<List<User>> getAll() async { ... }
@override
Future<void> save(User item) async { ... }
@override
Future<void> delete(int id) async { ... }
}
class ProductRepository implements Repository<Product> {
// 也必须实现所有 4 个方法
@override
Future<Product?> getById(int id) async { ... }
@override
Future<List<Product>> getAll() async { ... }
@override
Future<void> save(Product item) async { ... }
@override
Future<void> delete(int id) async { ... }
}
11. 快速记忆
| 概念 | 关键字 | 特点 |
|---|---|---|
| 抽象类 | abstract class |
不能实例化,定义规范 |
| 抽象方法 | void method(); |
只有声明没有实现,子类必须重写 |
| 实现接口 | implements |
必须重写所有抽象方法 |
| 继承父类 | extends |
获得功能,可选重写 |
🎯 什么时候用 abstract?
- 需要定义"模板/规范"时
- 不需要直接创建对象时🎯 什么时候用 implements?
- 需要强制某个类实现某些方法时
- 定义"接口"(Dart 没有 interface 关键字,用 abstract class 代替)🎯 extends vs implements
-extends:继承父类,获得功能,可用super
-implements:实现接口,必须重写所有方法





Comments NOTHING