1.Dart语言基础
一.Dart语言基础
面向Java开发者的Dart学习笔记,通过对比让你快速掌握Dart特性
1. 变量声明
Dart中的变量声明方式
// 方式1:显式类型声明(类似Java)
String name = "张三";
int age = 25;
double height = 1.75;
// 方式2:使用var(类型推断)
var city = "北京"; // 编译器推断为String
var score = 95.5; // 推断为double
// 方式3:使用dynamic(动态类型)
dynamic anything = "hello";
anything = 123; // 可以改变类型
anything = true; // 又可以变成布尔值
与Java的关键区别
| 特性 | Dart | Java |
|---|---|---|
| 类型推断 | 支持 var |
Java 10+ 支持 var |
| 动态类型 | 支持 dynamic |
不支持 |
| 所有类型 | 都是对象 | 有基本类型 |
重要提示:Dart中没有基本类型,int、double、bool 都是类,这也是为什么可以调用 5.toString()。
2. 常量声明 - const
const 表示编译时常量,值在编译时就必须确定。
// 正确用法
const PI = 3.14159;
const appName = "我的应用";
const List<int> emptyList = []; // 空列表常量
// 错误用法 - 编译错误
const currentTime = DateTime.now(); // ❌ 运行时才能确定的值
// const 创建不可变集合
const numbers = [1, 2, 3];
// numbers.add(4); // ❌ 不可变集合不能修改
与Java对比
Java中 final 和 static final 的组合才能达到类似效果:
// Java
public static final double PI = 3.14159;
3. 常量声明 - final
final 表示运行时常量,只能赋值一次。
// 正确用法
final currentTime = DateTime.now(); // ✅ 运行时确定没问题
final name = "李四";
final list = [1, 2, 3];
// final 变量可以修改引用对象的内部状态
final list = [1, 2, 3];
list.add(4); // ✅ 可以修改,因为是同一个列表对象
// list = [4, 5, 6]; // ❌ 不能重新赋值
// final 和 const 的区别
const constList = [1, 2, 3];
final finalList = [1, 2, 3];
constList.add(4); // ❌ 错误:const创建的是不可变集合
finalList.add(4); // ✅ 正确:finalList引用的集合是可变的
4. 字符串类型 - String
Dart的字符串功能非常强大,支持多种写法。
字符串定义
// 单引号(最常用)
String name = '张三';
// 双引号
String name2 = "李四";
// 三引号(多行字符串)
String multiLine = '''
这是第一行
这是第二行
这是第三行
''';
// 原始字符串(不转义)
String rawString = r'换行符是:\n,不会真的换行';
字符串插值(核心特性)
String name = "王五";
int age = 30;
// 基本插值:$变量名
print("姓名:$name"); // 输出:姓名:王五
// 复杂插值:${表达式}
print("明年年龄:${age + 1}"); // 输出:明年年龄:31
print("姓名大写:${name.toUpperCase()}"); // 输出:姓名大写:王五
// 对象属性插值
class Person {
String name;
Person(this.name);
}
var p = Person("赵六");
print("对象姓名:${p.name}"); // 输出:对象姓名:赵六
字符串操作
String str = "Hello Dart";
// 常用方法
print(str.length); // 10
print(str.toUpperCase()); // HELLO DART
print(str.toLowerCase()); // hello dart
print(str.contains("Dart")); // true
print(str.startsWith("He")); // true
print(str.endsWith("rt")); // true
print(str.substring(0, 5)); // Hello
// 字符串拼接
String s1 = "Hello";
String s2 = "World";
// 方式1:+ 运算符
String s3 = s1 + " " + s2; // Hello World
// 方式2:相邻字符串自动拼接
String s4 = "Hello" " " "World"; // Hello World
// 方式3:插值(推荐)
String s5 = "$s1 $s2"; // Hello World
5. 数字类型
Dart有两种数字类型:int 和 double,都是64位。
基本使用
// 整数
int age = 25;
int hex = 0xDEADBEEF; // 十六进制
// 浮点数
double height = 1.75;
double pi = 3.1415926;
double scientific = 1.42e5; // 科学计数法:142000
// 类型转换
int a = 10;
double b = a.toDouble(); // 10.0
double c = 3.14;
int d = c.toInt(); // 3(直接截断,不是四舍五入)
// 字符串转数字
String numStr = "123";
int e = int.parse(numStr); // 123
double f = double.parse("3.14"); // 3.14
// 数字转字符串
int g = 456;
String gStr = g.toString(); // "456"
String hStr = 3.14159.toStringAsFixed(2); // "3.14"
数值运算
int a = 10;
int b = 3;
print(a + b); // 13
print(a - b); // 7
print(a * b); // 30
print(a / b); // 3.3333333333333335(返回double)
print(a ~/ b); // 3(整除,返回int)
print(a % b); // 1(取余)
6. 布尔类型
bool 类型只有两个值:true 和 false。
基本使用
bool isActive = true;
bool isFinished = false;
// 布尔表达式
bool isEven = 10 % 2 == 0; // true
bool isGreater = 5 > 3; // true
重要:Dart的类型安全
// 在Java中,这可以运行
// if (1) { } // ❌ Dart中不行!
// 在Dart中,必须使用明确的布尔值
int count = 5;
if (count > 0) { // ✅ 正确:必须是比较表达式
print("count大于0");
}
// 检查空值
String? name;
if (name != null) { // ✅ 必须明确检查null
print(name.length);
}
// 简化写法(如果name不为null,才执行)
print(name?.length); // 安全调用运算符
7. 列表类型 - List
List相当于Java中的ArrayList,但功能更丰富。
创建List
// 方式1:字面量创建
List<int> numbers = [1, 2, 3, 4, 5];
var fruits = ['苹果', '香蕉', '橙子']; // 类型推断为List<String>
// 方式2:空列表
List<String> empty = []; // 推荐
var empty2 = <String>[]; // 也推荐,明确泛型类型
// 方式3:填充创建
var zeros = List.filled(5, 0); // [0, 0, 0, 0, 0]
var list = List.generate(5, (index) => index * 2); // [0, 2, 4, 6, 8]
// 方式4:不可变List
const constList = [1, 2, 3]; // 编译时常量,不可修改
常用操作
var list = [1, 2, 3];
// 添加元素
list.add(4); // [1, 2, 3, 4]
list.addAll([5, 6]); // [1, 2, 3, 4, 5, 6]
list.insert(0, 0); // 在索引0插入0:[0, 1, 2, 3, 4, 5, 6]
list.insertAll(2, [7, 8]); // [0, 1, 7, 8, 2, 3, 4, 5, 6]
// 删除元素
list.remove(7); // 删除值为7的元素
list.removeAt(0); // 删除索引0的元素
list.removeLast(); // 删除最后一个
list.removeRange(1, 3); // 删除索引1到2的元素
// 查询元素
print(list[0]); // 通过索引访问
print(list.indexOf(5)); // 查找元素索引
print(list.contains(3)); // 是否包含
print(list.length); // 长度
print(list.isEmpty); // 是否为空
print(list.isNotEmpty);// 是否不为空
高级特性
var list = [1, 2, 3, 4, 5];
// 扩展运算符(spread operator)
var list2 = [0, ...list]; // [0, 1, 2, 3, 4, 5]
// 空感知扩展
List<int>? nullableList;
var list3 = [1, 2, ...?nullableList]; // [1, 2](不会因为null崩溃)
// 集合中的if(条件添加)
var isActive = true;
var list4 = [
1,
2,
if (isActive) 3, // 如果isActive为true,才添加3
]; // [1, 2, 3]
// 集合中的for(循环添加)
var list5 = [
0,
for (var i = 1; i <= 3; i++) i, // 添加1,2,3
]; // [0, 1, 2, 3]
8. 字典类型 - Map
Map是键值对集合,类似Java的HashMap。
创建Map
// 方式1:字面量创建
Map<String, int> scores = {
'张三': 90,
'李四': 85,
'王五': 88
};
// 类型推断
var map = {
'name': '张三',
'age': 25,
'score': 95.5
}; // 推断为Map<String, Object>
// 方式2:构造器创建
var emptyMap = {}; // 注意:这是Map,不是Set!
var map2 = Map<String, int>();
var map3 = Map.from({'a': 1, 'b': 2});
// 方式3:不可变Map
const constMap = {'a': 1, 'b': 2};
常用操作
var map = {'name': '张三', 'age': 25};
// 添加/修改元素
map['score'] = 95; // 添加新键值对
map['age'] = 26; // 修改已有键的值
map.addAll({'city': '北京', 'gender': '男'});
// 获取元素
print(map['name']); // 张三(如果键不存在返回null)
print(map['weight']); // null(键不存在)
// 安全获取
var name = map['name'] ?? '未知'; // 如果为null,使用默认值
var weight = map['weight'] ?? 60; // 60
// 删除元素
map.remove('age'); // 删除age键
map.clear(); // 清空所有
// 遍历Map
map.forEach((key, value) {
print('$key: $value');
});
// 获取所有键或值
var keys = map.keys; // Iterable<String>
var values = map.values; // Iterable<Object>
高级特性
// Map中的if和for(类似List)
var condition = true;
var map = {
'a': 1,
'b': 2,
if (condition) 'c': 3, // 条件添加
for (var i = 4; i <= 5; i++) 'key$i': i, // 循环添加
}; // {'a': 1, 'b': 2, 'c': 3, 'key4': 4, 'key5': 5}
// 扩展合并Map
var map1 = {'a': 1, 'b': 2};
var map2 = {'b': 3, 'c': 4};
var merge = {...map1, ...map2}; // {'a': 1, 'b': 3, 'c': 4}
9. 动态类型 - dynamic
dynamic 是Dart的特殊类型,可以绕过类型检查。
基本使用
dynamic variable = "hello";
print(variable.length); // 5(String的length属性)
variable = 123;
print(variable.isEven); // false(int的isEven属性)
variable = true;
// print(variable.length); // 运行时错误!布尔没有length属性
// 对比:使用Object
Object obj = "hello";
// obj.length; // 编译错误!Object没有length属性
使用场景
// 场景1:与JSON交互
dynamic jsonData = jsonDecode('{"name":"张三","age":25}');
print(jsonData['name']); // 运行时才知道类型
// 场景2:混合类型集合(不推荐)
List<dynamic> mixed = [1, "hello", true, [1, 2, 3]];
// 场景3:泛型不确定时
T genericMethod<T>(T value) {
return value;
}
风险与注意事项
dynamic a = "123";
int b = a + 1; // ❌ 运行时错误:字符串不能加数字
// 更好的做法:使用类型检查
if (a is String) {
print(int.parse(a) + 1); // 124
}
10. 空安全机制
Dart 2.12+ 引入空安全,是语言最重要的特性之一。
可空类型和非空类型
// 默认不可空
String name = "张三"; // 不能赋值为null
// name = null; // ❌ 编译错误
// 可空类型:在类型后加?
String? nullableName; // 可以是String或null
nullableName = null; // ✅ 允许
nullableName = "李四"; // ✅ 允许
// 基本类型也是对象,同样遵循空安全
int? age;
double? height;
bool? isActive;
访问可空变量
String? name;
// 方法1:先判断再使用
if (name != null) {
print(name.length); // 自动推断为非空
}
// 方法2:安全调用运算符 ?.
print(name?.length); // 如果name为null,返回null,不会崩溃
// 方法3:提供默认值 ??
var length = name?.length ?? 0; // 如果name?.length为null,使用0
// 方法4:强制非空断言!(确定一定不为null时才用)
String? maybeName = getName();
if (someCondition) {
print(maybeName!.length); // 告诉编译器:我确定它不为null
}
延迟初始化
// late关键字:延迟初始化
class Person {
late String name; // 稍后初始化,使用前必须赋值
void setName(String newName) {
name = newName; // 可以在这里初始化
}
}
// 使用场景:依赖注入
class Database {
late DatabaseConnection connection;
void connect() {
connection = DatabaseConnection();
}
}
// 使用场景:循环引用
class A {
late B b;
}
class B {
late A a;
}
类型提升
String? nullableString = getString();
// Dart会自动进行类型提升
if (nullableString != null) {
// 在这里nullableString被提升为非空类型
print(nullableString.length); // 不需要?或!
}
// 逻辑运算符也会触发类型提升
if (nullableString is String) {
print(nullableString.length); // 自动提升
}
11. 算数运算符和赋值运算符
算数运算符
int a = 10;
int b = 3;
print(a + b); // 13
print(a - b); // 7
print(a * b); // 30
print(a / b); // 3.3333333333333335
print(a ~/ b); // 3(整除,Dart特有)
print(a % b); // 1
print(-a); // -10(一元负号)
// 自增自减
int c = 5;
print(c++); // 5(先使用,后增加)
print(++c); // 7(先增加,后使用)
print(c--); // 7(先使用,后减少)
print(--c); // 5(先减少,后使用)
赋值运算符
// 基本赋值
int a = 10;
// 复合赋值
a += 5; // a = a + 5
a -= 3; // a = a - 3
a *= 2; // a = a * 2
a ~/= 3; // a = a ~/ 3(整除)
a %= 4; // a = a % 4
// 空赋值运算符 ??=
String? name;
name ??= "张三"; // 如果name为null,赋值为"张三"
print(name); // 张三
name ??= "李四"; // name不为null,不赋值
print(name); // 还是张三
与Java对比
Dart特有的运算符:
~/:整除运算符??=:空赋值运算符
12. 比较运算符和逻辑运算符
比较运算符
int a = 5;
int b = 3;
print(a == b); // false(相等)
print(a != b); // true(不等)
print(a > b); // true(大于)
print(a < b); // false(小于)
print(a >= b); // true(大于等于)
print(a <= b); // false(小于等于)
// 注意:Dart中比较对象内容
var list1 = [1, 2, 3];
var list2 = [1, 2, 3];
print(list1 == list2); // false(不同对象)
print(list1.hashCode == list2.hashCode); // false
// 要比较内容,需要自己实现或使用特定方法
类型判断运算符
var value = "hello";
// is 判断类型
print(value is String); // true
print(value is int); // false
print(value is! int); // true(不是int)
// 类型转换
if (value is String) {
// 这里value自动转为String类型
print(value.length); // 可以直接调用String的方法
}
逻辑运算符
bool isSunny = true;
bool isWarm = false;
// 与运算 &&
print(isSunny && isWarm); // false
// 或运算 ||
print(isSunny || isWarm); // true
// 非运算 !
print(!isSunny); // false
print(!isWarm); // true
// 短路特性
var list;
print(list != null && list.isNotEmpty); // 如果list为null,不会执行后面的判断
13. if分支和三元运算符
if-else语句
int score = 85;
// 基本if
if (score >= 60) {
print("及格");
}
// if-else
if (score >= 90) {
print("优秀");
} else {
print("继续努力");
}
// if-else if-else
if (score >= 90) {
print("A");
} else if (score >= 80) {
print("B");
} else if (score >= 70) {
print("C");
} else if (score >= 60) {
print("D");
} else {
print("F");
}
三元运算符
int age = 20;
// 基本用法
String status = age >= 18 ? "成年" : "未成年";
print(status); // 成年
// 嵌套使用(不推荐,影响可读性)
String result = age > 60 ? "老年" : (age > 30 ? "中年" : "青年");
// 结合空安全
String? name;
String displayName = name ?? "访客"; // 如果name为null,使用"访客"
String displayName2 = name != null ? name : "访客"; // 等价于上面
14. 开关 - Switch
Dart的switch支持更多类型,且更安全。
基本使用
String grade = "A";
switch (grade) {
case "A":
print("优秀");
break;
case "B":
print("良好");
break;
case "C":
print("及格");
break;
case "D":
case "F": // 多个case共用同一个代码块
print("需要努力");
break;
default:
print("无效成绩");
}
支持的类型
// 整数
int number = 2;
switch (number) {
case 1:
print("一");
break;
case 2:
print("二");
break;
}
// 字符串(Java 7+也支持)
String command = "start";
switch (command) {
case "start":
case "go":
print("开始");
break;
case "stop":
print("停止");
break;
}
// 枚举
enum Color { red, green, blue }
Color color = Color.red;
switch (color) {
case Color.red:
print("红色");
break;
case Color.green:
print("绿色");
break;
case Color.blue:
print("蓝色");
break;
}
重要特性:必须覆盖所有可能
enum Status { pending, completed, failed }
String getMessage(Status status) {
// 如果枚举新增了值,这里会编译警告
switch (status) {
case Status.pending:
return "处理中";
case Status.completed:
return "完成";
case Status.failed:
return "失败";
}
// 不需要default,因为已经覆盖了所有枚举值
}
15. While循环
while循环
// 先判断,后执行
int i = 0;
while (i < 5) {
print("i = $i");
i++;
}
// 输出:0,1,2,3,4
// 可能一次都不执行
int j = 10;
while (j < 5) { // 条件为false,循环体不会执行
print("这行不会打印");
}
do-while循环
// 先执行一次,再判断
int i = 0;
do {
print("i = $i");
i++;
} while (i < 5);
// 输出:0,1,2,3,4
// 至少执行一次
int j = 10;
do {
print("至少打印一次"); // 会打印
} while (j < 5);
break和continue
// break:跳出整个循环
int i = 0;
while (i < 10) {
if (i == 5) {
break; // i等于5时退出循环
}
print(i);
i++;
}
// 输出:0,1,2,3,4
// continue:跳过本次循环
for (int j = 0; j < 10; j++) {
if (j % 2 == 0) {
continue; // 跳过偶数
}
print(j);
}
// 输出:1,3,5,7,9
16. For循环
标准for循环
// 基本语法
for (int i = 0; i < 5; i++) {
print("i = $i");
}
// 多个变量
for (int i = 0, j = 10; i < j; i++, j--) {
print("i = $i, j = $j");
}
for-in循环(增强for循环)
// 遍历List
var list = [1, 2, 3, 4, 5];
for (var item in list) {
print(item);
}
// 遍历Set
var set = {1, 2, 3};
for (var value in set) {
print(value);
}
// 遍历Map的键值对
var map = {'a': 1, 'b': 2};
for (var entry in map.entries) {
print('${entry.key}: ${entry.value}');
}
forEach方法
var list = [1, 2, 3];
// 使用匿名函数
list.forEach((item) {
print(item);
});
// 使用箭头函数
list.forEach((item) => print(item));
// 使用方法引用
list.forEach(print);
集合中的for(Dart 2.3+)
// 在集合字面量中使用for
var list = [1, 2, 3];
var newList = [
0,
for (var i in list) i * 2, // [2, 4, 6]
]; // [0, 2, 4, 6]
// 配合if使用
var condition = true;
var result = [
for (var i = 1; i <= 3; i++)
if (condition) i
else i * 10,
];
17. 函数定义
Dart中函数是一等公民,可以赋值给变量、作为参数传递。
基本函数定义
// 无返回值
void sayHello() {
print("Hello");
}
// 有返回值
int add(int a, int b) {
return a + b;
}
// 返回值类型可省略(不推荐)
multiply(int a, int b) {
return a * b; // 自动推断返回类型为int
}
函数也是对象
// 函数可以赋值给变量
var fun = sayHello;
fun(); // 调用函数
// 函数可以作为参数
void execute(Function callback) {
callback();
}
execute(sayHello);
// 函数可以作为返回值
Function getGreeting() {
return () => print("Hello");
}
var greet = getGreeting();
greet();
参数类型
// 必需参数(必须按顺序传递)
void printInfo(String name, int age) {
print("$name is $age years old");
}
// 调用时必须传递两个参数
printInfo("张三", 25);
箭头函数(简写)
// 当函数体只有一行表达式时
int add(int a, int b) => a + b;
// 等价于
int add(int a, int b) {
return a + b;
}
// 多个表达式?不,箭头函数只能有一个表达式
// void test() => { print("a"); print("b"); } // ❌ 错误
18. 函数必传参数和可选位置参数
Dart的函数参数分为:必传参数、可选位置参数、可选命名参数。
必传参数
// 所有参数默认都是必传的
void greet(String name, String message) {
print("$name: $message");
}
// 调用时必须传递所有参数
greet("张三", "你好");
// greet("张三"); // ❌ 错误:缺少参数
可选位置参数 []
// 使用[]包裹的参数是可选的
void sayHello(String name, [String? title, String? greeting]) {
var finalGreeting = greeting ?? "你好";
if (title != null) {
print("$title $name, $finalGreeting");
} else {
print("$name, $finalGreeting");
}
}
// 调用方式
sayHello("张三"); // 只传必传参数
sayHello("李四", "先生"); // 传一个可选参数
sayHello("王五", "博士", "早上好"); // 传两个可选参数
// 可选参数可以有默认值
void log(String message, [String level = "INFO"]) {
print("[$level] $message");
}
log("系统启动"); // [INFO] 系统启动
log("错误发生", "ERROR"); // [ERROR] 错误发生
注意事项
// 可选参数必须在必传参数之后
// void test([int a], int b) // ❌ 错误:可选参数不能在必传参数前面
// 多个可选参数,调用时按顺序匹配
void example(int a, [int? b, int? c, int? d]) {
print("a=$a, b=$b, c=$c, d=$d");
}
example(1); // a=1, b=null, c=null, d=null
example(1, 2); // a=1, b=2, c=null, d=null
example(1, 2, 3); // a=1, b=2, c=3, d=null
example(1, 2, 3, 4); // a=1, b=2, c=3, d=4
19. 函数的可选命名参数
命名参数是Dart的特色功能,让函数调用更具可读性。
基本用法 {}
// 使用{}包裹命名参数
void createUser({
required String name, // required表示必须传递
int? age, // 可选的命名参数
String? city,
String? job,
}) {
print("创建用户:$name, $age岁, 来自$city, 职业$job");
}
// 调用时指定参数名
createUser(
name: "张三",
age: 25,
city: "北京",
);
createUser(
name: "李四",
job: "工程师",
// 可以不按定义顺序
);
默认值
void configure({
String host = "localhost",
int port = 8080,
bool https = false,
}) {
print("连接:${https ? 'https' : 'http'}://$host:$port");
}
// 使用默认值
configure(); // http://localhost:8080
// 覆盖某些默认值
configure(host: "example.com", https: true); // https://example.com:8080
configure(port: 3000); // http://localhost:3000
混合参数类型
// 可以混合使用:必传参数 + 命名参数
void search(String query, {
int? limit,
bool caseSensitive = false,
}) {
print("搜索:$query, 限制:$limit, 区分大小写:$caseSensitive");
}
// 调用
search("dart"); // 只传必传参数
search("flutter", limit: 10); // 传必传+一个命名参数
search("java", caseSensitive: true); // 传必传+另一个命名参数
search("kotlin", limit: 5, caseSensitive: true); // 全部
位置参数 vs 命名参数
// 位置参数 - 适合参数少、意义明确的场景
void log(String message, [String level = "INFO"]) {
print("[$level] $message");
}
// 命名参数 - 适合参数多、可选性强的场景
void showDialog({
required String title,
String? content,
bool cancellable = true,
VoidCallback? onConfirm,
VoidCallback? onCancel,
}) {
// 实现弹窗逻辑
}
// 调用时一目了然
showDialog(
title: "确认删除",
content: "确定要删除这个文件吗?",
onConfirm: () => print("确认"),
onCancel: () => print("取消"),
);
20. 匿名函数和箭头函数
匿名函数(Lambda表达式)
// 基本匿名函数
var list = [1, 2, 3, 4, 5];
// 使用匿名函数作为参数
list.forEach((item) {
print(item * 2);
});
// 赋值给变量
var multiply = (int a, int b) {
return a * b;
};
print(multiply(3, 4)); // 12
// 无参数匿名函数
var greet = () {
print("Hello");
};
greet();
箭头函数(简洁写法)
// 当函数体只有一行表达式时
var add = (int a, int b) => a + b;
// 等价于
var add2 = (int a, int b) {
return a + b;
};
// 在集合操作中非常有用
var numbers = [1, 2, 3, 4, 5];
var doubled = numbers.map((n) => n * 2).toList(); // [2, 4, 6, 8, 10]
var evenNumbers = numbers.where((n) => n % 2 == 0).toList(); // [2, 4]
// 返回布尔值的箭头函数
var isEmpty = (String s) => s.isEmpty;
print(isEmpty("")); // true
闭包(Closure)
// 函数可以访问其定义时的作用域
Function makeCounter() {
int count = 0; // 这个变量被闭包捕获
return () {
count++; // 每次调用都会修改同一个count
return count;
};
}
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
print(counter()); // 3
// 另一个例子:函数工厂
Function makeAdder(int addBy) {
return (int i) => i + addBy; // addBy被闭包捕获
}
var add2 = makeAdder(2);
var add5 = makeAdder(5);
print(add2(3)); // 5
print(add5(3)); // 8
常用场景
// 1. 事件处理
button.onClick.listen((event) {
print("按钮被点击");
});
// 2. 排序
var people = ['张三', '李四', '王五'];
people.sort((a, b) => a.length.compareTo(b.length));
// 3. 过滤和转换
var numbers = [1, 2, 3, 4, 5];
var result = numbers
.where((n) => n > 2) // 过滤
.map((n) => n * 10) // 转换
.toList(); // [30, 40, 50]
// 4. 异步回调
Future.delayed(Duration(seconds: 1), () {
print("1秒后执行");
});
21. 类的定义
Dart是纯粹的面向对象语言,所有东西都是对象,所有对象都是类的实例。
基本类定义
class Person {
// 属性
String name = "";
int age = 0;
// 方法
void sayHello() {
print("大家好,我是$name,今年$age岁");
}
// getter
bool get isAdult => age >= 18;
// setter
set setName(String newName) {
name = newName;
}
}
// 使用类
var person = Person();
person.name = "张三";
person.age = 25;
person.sayHello(); // 大家好,我是张三,今年25岁
print(person.isAdult); // true
属性和方法的可见性
Dart没有public、protected、private关键字,使用下划线表示私有。
class BankAccount {
// 公有属性(默认)
String owner;
// 私有属性(以_开头,仅在当前文件可见)
double _balance = 0;
BankAccount(this.owner);
// 公有方法
void deposit(double amount) {
_balance += amount;
_logTransaction("存款", amount);
}
double get balance => _balance; // 只读getter
// 私有方法
void _logTransaction(String type, double amount) {
print("[$type] $amount,当前余额:$_balance");
}
}
var account = BankAccount("张三");
account.deposit(1000);
// print(account._balance); // ❌ 错误:不能访问私有属性
// account._logTransaction("测试", 100); // ❌ 错误:不能访问私有方法
print(account.balance); // 1000 ✅ 通过getter访问
计算属性(getter/setter)
class Rectangle {
double width;
double height;
Rectangle(this.width, this.height);
// getter:计算面积
double get area => width * height;
// getter:计算周长
double get perimeter => 2 * (width + height);
// setter:通过面积反推宽度(保持高度不变)
set area(double newArea) {
width = newArea / height;
}
// 带验证的setter
set setWidth(double value) {
if (value <= 0) {
throw ArgumentError("宽度必须大于0");
}
width = value;
}
}
var rect = Rectangle(10, 5);
print(rect.area); // 50(像属性一样访问)
print(rect.perimeter); // 30
rect.area = 100; // 通过setter设置
print(rect.width); // 20(因为100/5=20)
静态成员
class MathUtils {
// 静态常量
static const double pi = 3.14159;
// 静态方法
static int add(int a, int b) => a + b;
// 静态变量
static int _instanceCount = 0;
// 构造方法中计数
MathUtils() {
_instanceCount++;
}
// 静态getter
static int get instanceCount => _instanceCount;
}
// 使用静态成员
print(MathUtils.pi); // 3.14159
print(MathUtils.add(5, 3)); // 8
var utils1 = MathUtils();
var utils2 = MathUtils();
print(MathUtils.instanceCount); // 2
22. 类的默认构造函数
构造函数在创建对象时自动调用,用于初始化对象。
基本构造函数
class Person {
String name;
int age;
// 默认构造函数(与类同名)
Person(String name, int age) {
this.name = name;
this.age = age;
print("Person对象被创建");
}
}
var p = Person("张三", 25); // 输出:Person对象被创建
print(p.name); // 张三
语法糖写法(最常用)
class Person {
String name;
int age;
// 构造参数直接赋值给属性
Person(this.name, this.age);
// 甚至可以混合
Person.withDefaultAge(this.name) : age = 18;
}
var p1 = Person("张三", 25);
var p2 = Person.withDefaultAge("李四"); // age=18
构造函数初始化列表
class Point {
double x;
double y;
// 初始化列表:在构造函数体执行前初始化
Point.fromJson(Map<String, double> json)
: x = json['x'] ?? 0,
y = json['y'] ?? 0 {
print("Point初始化完成:($x, $y)");
}
// 使用assert验证参数
Point(this.x, this.y) : assert(x >= 0), assert(y >= 0);
// 重定向构造函数
Point.origin() : this(0, 0);
}
var p = Point.fromJson({'x': 10.5});
print(p.x); // 10.5
print(p.y); // 0
初始化列表的高级用法
class Person {
final String name; // final属性必须在构造函数中初始化
final int age;
final String id;
// 初始化列表可以:
// 1. 初始化final变量
// 2. 验证输入
// 3. 重定向构造函数
Person(this.name, this.age)
: id = "P${DateTime.now().millisecondsSinceEpoch}",
assert(age > 0, "年龄必须大于0") {
print("创建用户:$name");
}
// 初始化列表可以调用方法
Person.fromName(String name)
: this(name, _getDefaultAge()); // 调用静态方法
static int _getDefaultAge() => 18;
}
23. 类的命名构造函数
Dart支持多个构造函数,通过命名来区分。
基本命名构造函数
class Person {
String name;
int age;
String? email;
// 主构造函数
Person(this.name, this.age);
// 命名构造函数:创建未成年人
Person.child(String name)
: this.name = name,
this.age = 10;
// 命名构造函数:从JSON创建
Person.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'],
email = json['email'];
// 命名构造函数:创建默认用户
Person.guest()
: name = "访客",
age = 0;
void introduce() {
print("我是$name,${age}岁${email != null ? ', 邮箱:$email' : ''}");
}
}
// 使用不同的构造函数
var p1 = Person("张三", 25);
p1.introduce(); // 我是张三,25岁
var p2 = Person.child("小明");
p2.introduce(); // 我是小明,10岁
var p3 = Person.fromJson({
'name': '李四',
'age': 30,
'email': 'lisi@example.com'
});
p3.introduce(); // 我是李四,30岁, 邮箱:lisi@example.com
var p4 = Person.guest();
p4.introduce(); // 我是访客,0岁
工厂构造函数
工厂构造函数不一定会创建新实例,可以返回缓存的对象或子类实例。
class Singleton {
// 私有构造函数
Singleton._internal();
// 静态实例
static final Singleton _instance = Singleton._internal();
// 工厂构造函数
factory Singleton() {
return _instance; // 总是返回同一个实例
}
}
// 使用
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true(同一个对象)
更复杂的工厂构造函数示例
class Shape {
// 工厂构造函数可以返回子类
factory Shape(String type) {
switch (type) {
case 'circle':
return Circle();
case 'square':
return Square();
default:
throw ArgumentError('Unknown shape type');
}
}
}
class Circle implements Shape {
Circle();
}
class Square implements Shape {
Square();
}
// 使用
var shape = Shape('circle'); // 实际得到的是Circle对象
24. 类的构造函数语法糖写法
Dart提供了多种简洁的构造函数写法。
最简写法:this.参数
class Point {
double x;
double y;
// 最简洁的写法
Point(this.x, this.y);
// 等价于
// Point(double x, double y) {
// this.x = x;
// this.y = y;
// }
}
var p = Point(3, 4);
可选参数的构造函数
class Person {
String name;
int age;
String? email;
String? phone;
// 组合使用:必传参数 + 可选命名参数
Person(this.name, this.age, {this.email, this.phone});
// 或者使用可选位置参数
Person.withOptional(this.name, this.age, [this.email, this.phone]);
@override
String toString() {
return "$name($age) $email $phone";
}
}
// 调用
var p1 = Person("张三", 25);
var p2 = Person("李四", 30, email: "li@example.com");
var p3 = Person("王五", 28, email: "wang@ex.com", phone: "123456");
var p4 = Person.withOptional("赵六", 22, "zhao@ex.com");
初始化列表的语法糖
class Rectangle {
final double width;
final double height;
late final double area; // 稍后初始化
// 初始化列表中可以执行表达式
Rectangle(this.width, this.height) : area = width * height;
// 重定向构造函数
Rectangle.square(double size) : this(size, size);
// 条件初始化
Rectangle.fromWidth(double width)
: this(width, width * 0.618); // 黄金比例
}
const构造函数
class ImmutablePoint {
final double x;
final double y;
// const构造函数:所有属性必须是final
const ImmutablePoint(this.x, this.y);
// 常量构造函数不能有函数体
}
// 创建编译时常量
const origin = ImmutablePoint(0, 0);
const p1 = ImmutablePoint(1, 2);
const p2 = ImmutablePoint(1, 2);
print(identical(p1, p2)); // true(相同常量值复用同一对象)
更复杂的示例
class User {
final String id;
final String name;
final int createdAt;
String? avatar;
List<String> roles;
// 综合使用各种特性
const User({
required this.name,
this.avatar,
this.roles = const [],
}) : id = _generateId(),
createdAt = DateTime.now().millisecondsSinceEpoch;
// 私有静态方法
static String _generateId() {
return DateTime.now().millisecondsSinceEpoch.toString();
}
// 命名构造函数 + 重定向
User.admin(String name)
: this(
name: name,
roles: ['admin'],
avatar: 'default_admin.png',
);
// 工厂构造函数 + 缓存
static final Map<String, User> _cache = {};
factory User.fromCache(String id, String name) {
if (_cache.containsKey(id)) {
return _cache[id]!;
} else {
final user = User(name: name);
_cache[id] = user;
return user;
}
}
}
25. 类的公有属性和私有属性
Dart的访问控制基于文件级别,使用下划线_表示私有。
基本私有属性
// 文件:person.dart
class Person {
// 公有属性
String name;
// 私有属性(仅当前文件可见)
int _age;
String _id;
// 构造函数
Person(this.name, this._age) : _id = _generateId();
// 公有方法可以访问私有属性
void introduce() {
print("我是$name,年龄$_age,ID:$_id");
}
// getter提供只读访问
int get age => _age;
// setter可以添加验证
set age(int value) {
if (value < 0 || value > 150) {
throw ArgumentError("年龄无效");
}
_age = value;
}
// 私有方法
static String _generateId() {
return DateTime.now().millisecondsSinceEpoch.toString();
}
}
另一个文件使用
// 文件:main.dart
import 'person.dart';
void main() {
var p = Person("张三", 25);
// 可以访问公有属性和方法
print(p.name); // 张三
p.introduce(); // 我是张三,年龄25,ID:...
// 不能直接访问私有成员
// print(p._age); // ❌ 错误
// p._generateId(); // ❌ 错误
// 通过getter/setter访问
print(p.age); // 25
p.age = 30; // 通过setter修改
}
库级别的私有
// 文件:utils.dart
// 以下划线开头的顶级变量、函数也是私有的
String _secretKey = "123456";
void _privateFunction() {
print("这是私有函数");
}
// 公开的类可以访问同一文件中的私有成员
class DataProcessor {
void process() {
print(_secretKey); // ✅ 可以访问
_privateFunction(); // ✅ 可以访问
}
}
// 公开的函数
void publicFunction() {
print("公开函数");
}
封装的实践
class BankAccount {
// 私有属性
String _accountNumber;
double _balance = 0;
List<String> _transactions = [];
// 公开构造函数
BankAccount(this._accountNumber);
// 只读getter
String get accountNumber => _accountNumber;
double get balance => _balance;
// 只读集合的副本(保护内部数据)
List<String> get transactions => List.unmodifiable(_transactions);
// 带验证的方法
void deposit(double amount) {
if (amount <= 0) {
throw ArgumentError("存款金额必须大于0");
}
_balance += amount;
_addTransaction("存款", amount);
}
bool withdraw(double amount) {
if (amount <= 0) {
throw ArgumentError("取款金额必须大于0");
}
if (amount > _balance) {
return false;
}
_balance -= amount;
_addTransaction("取款", amount);
return true;
}
// 私有方法
void _addTransaction(String type, double amount) {
_transactions.add("$type: $amount,余额: $_balance");
}
}
// 使用
var account = BankAccount("123456");
account.deposit(1000);
account.withdraw(500);
print(account.balance); // 500
print(account.transactions); // ["存款: 1000,余额: 1000", "取款: 500,余额: 500"]
// 不能修改内部数据
// account._balance = 10000; // ❌ 错误
// account.transactions.add("非法操作"); // ❌ 错误(不可修改的集合)
26. 类的继承
Dart是单继承语言,使用extends关键字。
基本继承
class Animal {
String name;
Animal(this.name);
void eat() {
print("$name 在吃东西");
}
void sleep() {
print("$name 在睡觉");
}
}
class Dog extends Animal {
// 调用父类构造函数
Dog(String name) : super(name);
// 子类特有方法
void bark() {
print("$name 在汪汪叫");
}
// 重写父类方法
@override
void eat() {
super.eat(); // 调用父类方法
print("$name 吃得特别香");
}
}
void main() {
var dog = Dog("旺财");
dog.eat(); // 旺财在吃东西 → 旺财吃得特别香
dog.sleep(); // 旺财在睡觉
dog.bark(); // 旺财在汪汪叫
}
构造函数继承
class Person {
String name;
int age;
Person(this.name, this.age);
// 命名构造函数
Person.guest() : name = "访客", age = 0;
void introduce() {
print("我是$name,$age岁");
}
}
class Student extends Person {
String school;
// 必须调用父类构造函数
Student(String name, int age, this.school) : super(name, age);
// 调用父类命名构造函数
Student.guest(String school)
: this.school = school,
super.guest();
// 调用父类构造函数 + 初始化列表
Student.fromName(String name)
: school = "未知学校",
super(name, 18);
@override
void introduce() {
super.introduce();
print("我在$school上学");
}
}
方法重写的规则
class Parent {
// 可重写的方法
void normalMethod() {
print("父类普通方法");
}
// 不可重写的方法(使用final)
final void cannotOverride() {
print("这个方法不能重写");
}
// 非虚方法(static不能被继承)
static void staticMethod() {
print("静态方法");
}
}
class Child extends Parent {
@override
void normalMethod() {
print("子类重写的方法");
}
// @override
// void cannotOverride() {} // ❌ 错误:不能重写final方法
// @override
// static void staticMethod() {} // ❌ 错误:不能重写静态方法
}
类型检查和转换
class Animal {}
class Dog extends Animal {
void bark() => print("汪汪");
}
class Cat extends Animal {
void meow() => print("喵喵");
}
void main() {
Animal animal = Dog();
// is 检查类型
print(animal is Dog); // true
print(animal is Cat); // false
// as 类型转换
if (animal is Dog) {
// 自动转换(is检查后自动提升)
animal.bark(); // 可以直接调用Dog的方法
}
// 强制转换(不安全)
var dog = animal as Dog;
dog.bark();
// 错误的转换会抛出异常
// var cat = animal as Cat; // ❌ 运行时错误
}
继承与组合的选择
// 继承:is-a关系
class Vehicle {
void move() => print("移动");
}
class Car extends Vehicle {
void honk() => print("按喇叭");
}
// 组合:has-a关系
class Engine {
void start() => print("引擎启动");
}
class Bicycle {
Engine? engine; // 可选:自行车可能有引擎吗?
void ride() => print("骑行");
}
27. 类的多态
多态允许使用父类类型的变量引用子类对象。
基本多态
abstract class Shape {
double get area;
void draw() {
print("绘制形状");
}
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double get area => 3.14 * radius * radius;
@override
void draw() {
print("绘制圆形,半径:$radius");
}
void roll() {
print("圆形可以滚动");
}
}
class Square extends Shape {
double side;
Square(this.side);
@override
double get area => side * side;
@override
void draw() {
print("绘制正方形,边长:$side");
}
}
void main() {
// 多态:使用父类类型
Shape shape1 = Circle(5);
Shape shape2 = Square(4);
// 调用重写的方法
shape1.draw(); // 绘制圆形
shape2.draw(); // 绘制正方形
// 访问属性
print(shape1.area); // 78.5
print(shape2.area); // 16
// 不能调用子类特有方法
// shape1.roll(); // ❌ 错误
// 如果需要调用子类方法,需要类型转换
if (shape1 is Circle) {
shape1.roll(); // ✅ is检查后自动转换
}
}
参数多态
// 方法参数使用父类类型,可以接收任何子类对象
void printArea(Shape shape) {
print("面积:${shape.area}");
shape.draw();
}
void main() {
printArea(Circle(5));
printArea(Square(4));
}
// 集合多态
List<Shape> shapes = [
Circle(3),
Square(5),
Circle(2),
];
// 遍历集合
shapes.forEach((shape) {
print(shape.area);
});
运行时类型信息
void processShape(Shape shape) {
// 获取运行时类型
print(shape.runtimeType); // Circle 或 Square
// 类型判断
if (shape is Circle) {
// 在这里可以访问Circle的特有方法
shape.roll();
} else if (shape is Square) {
// Square特有的处理
print("正方形对角线:${shape.side * 1.414}");
}
}
多态的实际应用
// 策略模式
abstract class PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
String cardNumber;
CreditCardPayment(this.cardNumber);
@override
void pay(double amount) {
print("使用信用卡 $cardNumber 支付 $amount 元");
}
}
class AlipayPayment implements PaymentStrategy {
String account;
AlipayPayment(this.account);
@override
void pay(double amount) {
print("使用支付宝 $account 支付 $amount 元");
}
}
class ShoppingCart {
List<double> items = [];
void checkout(PaymentStrategy payment) {
double total = items.reduce((a, b) => a + b);
payment.pay(total);
}
}
// 使用
var cart = ShoppingCart();
cart.items = [100, 200, 50];
// 可以轻松切换支付方式
cart.checkout(CreditCardPayment("1234-5678"));
cart.checkout(AlipayPayment("user@example.com"));
多态与工厂模式
// 工厂模式结合多态
class ShapeFactory {
static Shape createShape(String type, double size) {
switch (type) {
case 'circle':
return Circle(size);
case 'square':
return Square(size);
default:
throw ArgumentError('Unknown shape type');
}
}
}
// 客户端代码不知道具体类型
List<Shape> createShapes() {
return [
ShapeFactory.createShape('circle', 5),
ShapeFactory.createShape('square', 4),
ShapeFactory.createShape('circle', 3),
];
}
// 所有形状都可以统一处理
void main() {
var shapes = createShapes();
shapes.forEach((shape) {
print("面积:${shape.area}");
});
}
28. 类的抽象和接口
Dart中抽象类和接口的概念与其他语言略有不同。
抽象类
使用abstract关键字定义抽象类,可以包含抽象方法和具体方法。
abstract class Animal {
// 抽象属性
String name;
// 构造函数(抽象类可以有构造函数)
Animal(this.name);
// 抽象方法
void makeSound();
// 具体方法
void sleep() {
print("$name 在睡觉");
}
// 抽象getter
int get legCount;
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void makeSound() {
print("$name 汪汪叫");
}
@override
int get legCount => 4;
}
class Bird extends Animal {
Bird(String name) : super(name);
@override
void makeSound() {
print("$name 叽叽喳喳");
}
@override
int get legCount => 2;
}
// 不能实例化抽象类
// var animal = Animal("动物"); // ❌ 错误
var dog = Dog("旺财");
dog.makeSound(); // 旺财汪汪叫
dog.sleep(); // 旺财在睡觉
print(dog.legCount); // 4
接口(Implicit interfaces)
Dart中没有interface关键字,每个类都隐式定义了一个接口。
// 这个类同时也是一个接口
class Flyable {
void fly() {
print("飞行中");
}
void land() {
print("降落");
}
}
// 使用implements实现接口
class Bird implements Flyable {
@override
void fly() {
print("小鸟在飞");
}
@override
void land() {
print("小鸟降落");
}
}
// 可以实现多个接口
class Swimmable {
void swim() {
print("游泳中");
}
}
class Duck implements Flyable, Swimmable {
@override
void fly() {
print("鸭子飞");
}
@override
void land() {
print("鸭子降落");
}
@override
void swim() {
print("鸭子游泳");
}
}
抽象类 vs 接口
// 抽象类:提供部分实现
abstract class Database {
// 抽象方法
void connect();
void disconnect();
// 具体方法
void executeQuery(String sql) {
connect();
print("执行SQL: $sql");
disconnect();
}
}
class MySQL extends Database {
@override
void connect() {
print("连接MySQL数据库");
}
@override
void disconnect() {
print("断开MySQL连接");
}
}
// 接口:只定义规范
class JsonSerializable {
Map<String, dynamic> toJson();
void fromJson(Map<String, dynamic> json);
}
class User implements JsonSerializable {
String name;
int age;
User(this.name, this.age);
@override
Map<String, dynamic> toJson() {
return {'name': name, 'age': age};
}
@override
void fromJson(Map<String, dynamic> json) {
name = json['name'];
age = json['age'];
}
}
实际应用示例
// 定义多个接口
abstract class Drawable {
void draw();
}
abstract class Resizable {
void resize(double factor);
}
abstract class Colorable {
void setColor(String color);
}
// 类可以实现多个接口
class Rectangle implements Drawable, Resizable, Colorable {
double width;
double height;
String color = "black";
Rectangle(this.width, this.height);
@override
void draw() {
print("绘制${color}矩形: ${width}x${height}");
}
@override
void resize(double factor) {
width *= factor;
height *= factor;
}
@override
void setColor(String color) {
this.color = color;
}
}
// 另一个类只实现部分接口
class Text implements Drawable {
String content;
Text(this.content);
@override
void draw() {
print("绘制文本: $content");
}
}
接口的默认实现
// 可以提供默认实现
class Logger {
void log(String message) {
print("LOG: $message");
}
void error(String message) {
print("ERROR: $message");
}
}
// 如果只想要接口,不想继承实现,使用implements
class FileLogger implements Logger {
@override
void log(String message) {
// 必须自己实现所有方法
print("File: $message");
}
@override
void error(String message) {
print("File Error: $message");
}
}
// 如果想复用实现,使用extends
class ConsoleLogger extends Logger {
// 自动继承log和error的实现
// 可以重写需要的方法
@override
void log(String message) {
print("Console: $message");
}
}
29. 类的混入
Mixin是Dart中代码复用的强大特性,可以实现类似多继承的效果。
基本Mixin
使用mixin关键字定义,用with关键字使用。
mixin Flyable {
void fly() {
print("飞行中");
}
void land() {
print("降落");
}
}
mixin Swimmable {
void swim() {
print("游泳中");
}
void dive() {
print("潜水");
}
}
class Bird with Flyable {
String name;
Bird(this.name);
void chirp() {
print("$name 叽叽喳喳");
}
}
class Duck with Flyable, Swimmable {
String name;
Duck(this.name);
@override
void fly() {
print("$name 在飞");
}
}
void main() {
var bird = Bird("小鸟");
bird.fly(); // 飞行中
bird.chirp(); // 小鸟叽叽喳喳
var duck = Duck("鸭子");
duck.fly(); // 鸭子 在飞
duck.swim(); // 游泳中
duck.dive(); // 潜水
}
Mixin的限制和特性
// Mixin不能有构造函数
mixin Playable {
// ❌ 不能有构造函数
// Playable() {}
// 可以有属性
String game = "默认游戏";
// 可以有方法
void play() {
print("玩$game");
}
// 可以有抽象方法
void stop();
}
class Child with Playable {
@override
void stop() {
print("停止游戏");
}
}
// Mixin可以指定类型约束(只能用于特定类的子类)
mixin Walkable on Animal {
void walk() {
print("$name 在走路");
}
}
class Animal {
String name;
Animal(this.name);
}
class Person extends Animal with Walkable {
Person(String name) : super(name);
}
// ❌ Dog没有继承Animal,不能使用Walkable
// class Dog with Walkable {} // 错误
解决命名冲突
mixin A {
void test() {
print("A的test");
}
}
mixin B {
void test() {
print("B的test");
}
}
// 后混入的覆盖前面的
class C with A, B {
// 使用的是B的test
}
class D with B, A {
// 使用的是A的test
}
class E with A, B {
@override
void test() {
print("自己的test");
super.test(); // 调用的是B的test(最后一个混入的)
}
}
void main() {
C().test(); // B的test
D().test(); // A的test
E().test(); // 自己的test → B的test
}
实际应用场景
// 1. 日志功能混入
mixin Logger {
void log(String message) {
var time = DateTime.now().toString().substring(0, 19);
print("[$time] $message");
}
void error(String message) {
log("ERROR: $message");
}
}
class UserService with Logger {
void createUser(String name) {
log("创建用户: $name");
// 业务逻辑...
log("用户创建成功");
}
}
// 2. 验证功能混入
mixin Validator {
bool isEmail(String value) {
return value.contains('@') && value.contains('.');
}
bool isPhone(String value) {
return value.length == 11 && value.startsWith('1');
}
bool notEmpty(String value) {
return value.isNotEmpty;
}
}
class RegisterForm with Validator {
String email = "";
String phone = "";
bool validate() {
if (!notEmpty(email)) {
error("邮箱不能为空");
return false;
}
if (!isEmail(email)) {
error("邮箱格式不正确");
return false;
}
return true;
}
void error(String msg) => print(msg);
}
// 3. 缓存功能混入
mixin Cacheable<T> {
Map<String, T> _cache = {};
T? getFromCache(String key) {
return _cache[key];
}
void addToCache(String key, T value) {
_cache[key] = value;
print("已缓存: $key");
}
void clearCache() {
_cache.clear();
}
}
class DataRepository with Cacheable<Map<String, dynamic>> {
Future<Map<String, dynamic>> fetchData(String id) {
// 先查缓存
var cached = getFromCache(id);
if (cached != null) {
return Future.value(cached);
}
// 模拟网络请求
return Future.delayed(Duration(seconds: 1), () {
var data = {'id': id, 'name': '数据$id'};
addToCache(id, data);
return data;
});
}
}
Mixin vs 继承 vs 接口
// 继承:is-a关系
class Vehicle {
void move() => print("移动");
}
class Car extends Vehicle {
void honk() => print("鸣笛");
}
// 接口:can-do关系
mixin Drivable {
void drive();
}
mixin Parkable {
void park();
}
// Mixin:has-feature关系
class Tesla extends Vehicle with Drivable, Parkable {
@override
void drive() {
print("自动驾驶");
}
@override
void park() {
print("自动泊车");
}
}
30. 泛型
泛型允许类、接口和方法在定义时使用类型参数,提高代码复用性和类型安全性。
泛型类
// 简单的泛型类
class Box<T> {
T value;
Box(this.value);
T getValue() => value;
void setValue(T newValue) {
value = newValue;
}
}
// 使用
var intBox = Box<int>(123);
print(intBox.getValue()); // 123
// intBox.setValue("hello"); // ❌ 类型错误
var stringBox = Box<String>("Hello");
print(stringBox.getValue()); // Hello
// 类型推断
var autoBox = Box("自动推断"); // Box<String>
多个类型参数
class Pair<K, V> {
K key;
V value;
Pair(this.key, this.value);
@override
String toString() => "Pair($key: $value)";
// 泛型方法
Pair<U, V> mapKey<U>(U Function(K) transform) {
return Pair(transform(key), value);
}
}
// 使用
var pair = Pair<String, int>("age", 25);
print(pair); // Pair(age: 25)
var mapped = pair.mapKey((key) => key.length);
print(mapped); // Pair(3: 25)
泛型约束
// 限制类型参数必须是某个类的子类
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
double get squared => value * value; // 可以调用num的方法
}
// 正确
var intBox = NumberBox<int>(5);
var doubleBox = NumberBox<double>(3.14);
// 错误
// var stringBox = NumberBox<String>("hello"); // ❌ 编译错误
// 多约束
class MyClass<T extends Object> {
// T必须是非空类型
}
// 接口约束
mixin Flyable {}
class Bird with Flyable {}
class Cage<T extends Flyable> {
T animal;
Cage(this.animal);
}
// 正确
var cage = Cage(Bird());
// 错误
// var cage2 = Cage(Object()); // ❌
泛型方法
// 泛型函数
T first<T>(List<T> list) {
if (list.isEmpty) {
throw StateError("列表为空");
}
return list[0];
}
// 使用
var numbers = [1, 2, 3];
var firstNumber = first<int>(numbers); // 1
// 类型推断
var firstString = first(["a", "b", "c"]); // "a"
// 泛型工具函数
List<T> filter<T>(List<T> items, bool Function(T) predicate) {
var result = <T>[];
for (var item in items) {
if (predicate(item)) {
result.add(item);
}
}
return result;
}
// 使用
var evens = filter<int>([1, 2, 3, 4, 5], (n) => n % 2 == 0);
print(evens); // [2, 4]
泛型与集合
// List
List<int> numbers = [1, 2, 3];
var strings = <String>["a", "b"]; // 另一种写法
// Set
Set<int> uniqueNumbers = {1, 2, 3};
var uniqueStrings = <String>{"a", "b"};
// Map
Map<String, int> scores = {"张三": 90, "李四": 85};
var map = <String, dynamic>{"name": "张三", "age": 25};
// 泛型集合操作
List<int> list1 = [1, 2, 3];
List<int> list2 = [4, 5, 6];
List<int> combined = [...list1, ...list2];
var doubled = list1.map((n) => n * 2).toList(); // [2, 4, 6]
var sum = list1.reduce((a, b) => a + b); // 6
泛型的高级用法
// 类型安全的建造者模式
class Builder<T> {
T? _value;
Builder<T> withValue(T value) {
_value = value;
return this;
}
T build() {
if (_value == null) {
throw StateError("Value not set");
}
return _value!;
}
}
// 泛型单例工厂
class Singleton<T> {
static final Map<Type, Object> _instances = {};
factory Singleton(T instance) {
var type = T;
if (!_instances.containsKey(type)) {
_instances[type] = instance;
}
return Singleton._internal();
}
Singleton._internal();
T? get() => _instances[T] as T?;
}
// 泛型缓存
class Cache<T> {
final Map<String, T> _cache = {};
T? get(String key) => _cache[key];
void set(String key, T value) {
_cache[key] = value;
}
void clear() => _cache.clear();
List<T> getAll() => _cache.values.toList();
}
泛型与类型系统
// 协变
class Animal {}
class Dog extends Animal {}
List<Animal> animals = <Dog>[]; // ✅ Dart支持协变
// 类型通配符(类似Java的?)
void printList(List<Object> list) {
print(list);
}
// 可以使用List<dynamic>
var strings = <String>["a", "b"];
printList(strings); // ✅ 可以
// 类型检查
void checkType<T>(T value) {
print(value is T); // true
print(value.runtimeType); // 实际类型
}
// 泛型函数类型
typedef Mapper<S, T> = T Function(S source);
List<T> mapList<S, T>(List<S> list, Mapper<S, T> mapper) {
return list.map(mapper).toList();
}
// 使用
var ints = [1, 2, 3];
var strings2 = mapList<int, String>(ints, (i) => "数字$i");
print(strings2); // [数字1, 数字2, 数字3]
31. 异步编程
Dart是单线程模型,但通过事件循环支持异步操作。
异步基础
// 同步代码会阻塞
void slowOperation() {
sleep(Duration(seconds: 2)); // 阻塞2秒
print("操作完成");
}
// 异步代码不会阻塞
Future<void> asyncOperation() async {
await Future.delayed(Duration(seconds: 2)); // 非阻塞等待
print("异步操作完成");
}
void main() {
print("开始");
asyncOperation();
print("结束");
// 输出顺序:
// 开始
// 结束
// (2秒后) 异步操作完成
}
事件循环
import 'dart:async';
void demonstrateEventLoop() {
print("1. 同步代码");
Future(() => print("2. 微任务队列外的Future"));
scheduleMicrotask(() => print("3. 微任务"));
Future.delayed(Duration.zero, () => print("4. 延迟Future"));
Future.microtask(() => print("5. 微任务Future"));
print("6. 更多同步代码");
}
// 输出顺序:
// 1. 同步代码
// 6. 更多同步代码
// 3. 微任务
// 5. 微任务Future
// 2. 微任务队列外的Future
// 4. 延迟Future
常见异步场景
// 1. 网络请求
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return "数据";
}
// 2. 文件操作
import 'dart:io';
Future<void> readFile() async {
var file = File('test.txt');
if (await file.exists()) {
var content = await file.readAsString();
print(content);
}
}
// 3. 定时器
Timer.periodic(Duration(seconds: 1), (timer) {
print("每秒执行一次");
if (/*某个条件*/) {
timer.cancel();
}
});
// 4. 流式数据
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(milliseconds: 500));
yield i; // 发射数据
}
}
错误处理
Future<void> asyncWithError() async {
try {
var result = await riskyOperation();
print("成功: $result");
} on TimeoutException catch (e) {
print("超时: $e");
} catch (e, stackTrace) {
print("其他错误: $e");
print(stackTrace);
} finally {
print("清理资源");
}
}
// 或者使用catchError
riskyOperation()
.then((value) => print("成功: $value"))
.catchError((error) => print("错误: $error"))
.whenComplete(() => print("完成"));
32. Future基本使用
Future表示一个异步操作的结果,类似于Java的Future/CompletableFuture。
创建Future
// 1. 直接创建已完成的值
Future.value(42).then((v) => print(v));
// 2. 创建错误
Future.error("出错了").catchError((e) => print(e));
// 3. 延迟执行
Future.delayed(Duration(seconds: 1), () {
return "1秒后返回";
}).then((msg) => print(msg));
// 4. 异步函数自动返回Future
Future<String> fetchData() {
return Future(() {
// 耗时操作
return "数据";
});
}
// 5. 使用async标记
Future<String> fetchDataAsync() async {
await Future.delayed(Duration(seconds: 1));
return "数据";
}
Future的状态
void checkFutureStates() {
// 未完成
var future1 = Future.delayed(Duration(seconds: 1), () => "完成");
// 已完成(有值)
var future2 = Future.value("已有值");
// 已完成(有错误)
var future3 = Future.error(Exception("错误"));
// 检查状态
future2.then((value) {
print("已完成: $value");
}).catchError((e) {
print("错误: $e");
});
}
Future的常用方法
void futureMethods() {
// then - 处理成功结果
Future.value(10)
.then((value) => value * 2)
.then((value) => print(value)); // 20
// catchError - 处理错误
Future.error("错误")
.catchError((e) => "已恢复")
.then((v) => print(v)); // 已恢复
// whenComplete - 无论成功失败都会执行
Future.delayed(Duration(seconds: 1))
.whenComplete(() => print("完成"));
// timeout - 超时处理
Future.delayed(Duration(seconds: 3))
.timeout(Duration(seconds: 2), onTimeout: () {
print("超时");
return null;
});
}
等待多个Future
void multipleFutures() {
Future<String> fetchUser() async {
await Future.delayed(Duration(seconds: 1));
return "张三";
}
Future<int> fetchAge() async {
await Future.delayed(Duration(seconds: 2));
return 25;
}
// Future.wait - 等待所有完成
Future.wait([fetchUser(), fetchAge()])
.then((results) {
print("用户: ${results[0]}, 年龄: ${results[1]}");
});
// Future.any - 只要有一个完成
Future.any([fetchUser(), fetchAge()])
.then((first) => print("最先完成: $first"));
// Future.doWhile - 循环执行
int count = 0;
Future.doWhile(() async {
if (count < 3) {
await Future.delayed(Duration(seconds: 1));
print("计数: ${++count}");
return true; // 继续循环
}
return false; // 停止循环
});
}
实际应用示例
class UserService {
Future<Map<String, dynamic>> fetchUser(int id) async {
// 模拟网络请求
await Future.delayed(Duration(seconds: 1));
if (id <= 0) {
throw Exception("无效的用户ID");
}
return {
'id': id,
'name': '用户$id',
'age': 20 + id,
};
}
Future<void> processUser() async {
try {
var user = await fetchUser(123);
print("获取用户成功: $user");
} catch (e) {
print("获取用户失败: $e");
}
}
}
33. Future链式调用
Future的链式调用可以优雅地处理多个异步操作。
基本链式调用
void basicChaining() {
Future<String> step1() async {
await Future.delayed(Duration(seconds: 1));
return "步骤1结果";
}
Future<String> step2(String input) async {
await Future.delayed(Duration(seconds: 1));
return "$input → 步骤2结果";
}
Future<String> step3(String input) async {
await Future.delayed(Duration(seconds: 1));
return "$input → 步骤3结果";
}
// 链式调用
step1()
.then((result1) => step2(result1))
.then((result2) => step3(result2))
.then((finalResult) {
print("最终结果: $finalResult");
})
.catchError((error) {
print("出错: $error");
})
.whenComplete(() {
print("所有步骤完成");
});
}
错误处理链
void errorHandlingChain() {
Future<String> riskyStep1() async {
await Future.delayed(Duration(seconds: 1));
throw Exception("步骤1失败");
}
Future<String> step2() async {
await Future.delayed(Duration(seconds: 1));
return "步骤2";
}
riskyStep1()
.then((result) => step2()) // 不会执行
.then((result) => print("成功: $result"))
.catchError((error) {
print("捕获错误: $error");
return "默认值"; // 可以恢复
})
.then((result) {
print("恢复后: $result"); // 默认值
});
}
条件分支
void conditionalChaining() {
Future<bool> checkPermission(String user) async {
await Future.delayed(Duration(milliseconds: 500));
return user == "admin";
}
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return "敏感数据";
}
Future<String> fetchPublicData() async {
await Future.delayed(Duration(milliseconds: 500));
return "公开数据";
}
checkPermission("guest")
.then((hasPermission) {
if (hasPermission) {
return fetchData();
} else {
return fetchPublicData();
}
})
.then((data) {
print("获取到: $data");
});
}
复杂的链式操作
class OrderProcessor {
Future<int> createOrder(String userId) async {
await Future.delayed(Duration(seconds: 1));
return 1001; // 订单号
}
Future<bool> checkInventory(int orderId, List<String> items) async {
await Future.delayed(Duration(seconds: 1));
return items.length < 5; // 简单逻辑
}
Future<double> calculatePrice(int orderId) async {
await Future.delayed(Duration(seconds: 1));
return 299.0;
}
Future<bool> processPayment(int orderId, double amount) async {
await Future.delayed(Duration(seconds: 2));
return true; // 支付成功
}
Future<void> sendNotification(int orderId) async {
await Future.delayed(Duration(milliseconds: 500));
print("订单 $orderId 通知已发送");
}
Future<void> processOrder(String userId, List<String> items) {
return createOrder(userId)
.then((orderId) {
print("订单创建: $orderId");
return checkInventory(orderId, items).then((inStock) {
if (!inStock) throw Exception("库存不足");
return orderId;
});
})
.then((orderId) {
return calculatePrice(orderId).then((price) {
print("订单金额: $price");
return processPayment(orderId, price).then((paid) {
if (!paid) throw Exception("支付失败");
return orderId;
});
});
})
.then((orderId) => sendNotification(orderId))
.then((_) => print("订单处理完成"))
.catchError((e) {
print("订单处理失败: $e");
});
}
}
Future链 vs async/await
// 链式调用风格
Future<String> getUserDataChain(String id) {
return fetchUser(id)
.then((user) => fetchUserPosts(user))
.then((posts) => formatPosts(posts))
.catchError((e) => "获取数据失败: $e");
}
// async/await风格(更清晰)
Future<String> getUserDataAsync(String id) async {
try {
var user = await fetchUser(id);
var posts = await fetchUserPosts(user);
return await formatPosts(posts);
} catch (e) {
return "获取数据失败: $e";
}
}
34. Future的async和await
async/await是Dart中处理异步代码的语法糖,让异步代码看起来像同步代码。
基本语法
// 定义异步函数
Future<String> fetchData() async {
// await 等待Future完成
var result = await Future.delayed(
Duration(seconds: 1),
() => "数据",
);
return result; // 自动包装成Future
}
// 调用
void main() async {
print("开始获取数据");
var data = await fetchData();
print("获取到: $data");
}
// 错误处理
Future<void> processWithError() async {
try {
var data = await riskyOperation();
print("成功: $data");
} catch (e) {
print("失败: $e");
} finally {
print("清理资源");
}
}
多个await
Future<void> fetchUserData() async {
print("开始获取用户数据");
// 顺序执行(总耗时2秒)
var user = await fetchUser(); // 1秒
var posts = await fetchPosts(user); // 1秒
print("用户: $user");
print("帖子数: ${posts.length}");
}
Future<void> fetchParallel() async {
print("开始并行获取");
// 并行执行(总耗时1秒)
var userFuture = fetchUser(); // 1秒
var postsFuture = fetchAllPosts(); // 1秒
// 等待两个Future完成
var user = await userFuture;
var posts = await postsFuture;
print("用户: $user");
print("帖子数: ${posts.length}");
}
实际应用示例
class ApiClient {
Future<Map<String, dynamic>> get(String url) async {
await Future.delayed(Duration(seconds: 1));
return {'url': url, 'data': '模拟数据'};
}
}
class UserRepository {
final ApiClient api = ApiClient();
// 获取用户信息
Future<Map<String, dynamic>> getUser(int id) async {
try {
var response = await api.get('/users/$id');
return response;
} catch (e) {
throw Exception("获取用户失败: $e");
}
}
// 获取用户的完整资料(包含帖子、关注者等)
Future<Map<String, dynamic>> getUserProfile(int id) async {
try {
// 并行获取多个数据
var userFuture = getUser(id);
var postsFuture = api.get('/users/$id/posts');
var followersFuture = api.get('/users/$id/followers');
// 等待所有请求完成
var results = await Future.wait([
userFuture,
postsFuture,
followersFuture,
]);
return {
'user': results[0],
'posts': results[1]['data'],
'followers': results[2]['data'],
};
} catch (e) {
throw Exception("获取用户资料失败: $e");
}
}
}
// 使用示例
void main() async {
var repo = UserRepository();
try {
var profile = await repo.getUserProfile(123);
print("用户资料: $profile");
} catch (e) {
print("错误: $e");
}
}
循环中的异步
// 顺序执行(慢)
Future<void> processSequential(List<int> ids) async {
for (var id in ids) {
var data = await fetchDataById(id);
print("处理ID $id: $data");
}
}
// 并行执行(快)
Future<void> processParallel(List<int> ids) async {
var futures = ids.map((id) => fetchDataById(id));
var results = await Future.wait(futures);
for (var i = 0; i < ids.length; i++) {
print("处理ID ${ids[i]}: ${results[i]}");
}
}
// 控制并发数量
Future<void> processBatched(List<int> ids, int batchSize) async {
for (var i = 0; i < ids.length; i += batchSize) {
var batch = ids.skip(i).take(batchSize);
var futures = batch.map((id) => fetchDataById(id));
var results = await Future.wait(futures);
for (var j = 0; j < batch.length; j++) {
print("处理ID ${batch.elementAt(j)}: ${results[j]}");
}
}
}
异步生成器(Stream)
// 异步生成器返回Stream
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i; // 发射值
}
}
// 使用Stream
Future<void> useStream() async {
var stream = countStream(5);
// 方式1:await for
await for (var value in stream) {
print("收到: $value");
}
// 方式2:监听
stream.listen(
(value) => print("值: $value"),
onError: (e) => print("错误: $e"),
onDone: () => print("完成"),
);
}