Flutter之Dart常用数据类型(数字、类型转换)
1. Dart 数字类型简介
类型 | 说明 | 备注 |
---|---|---|
int |
64位整数 | 适合整数,索引、计数等场景 |
double |
64位浮点数(双精度) | 适合小数、精确度要求不高的场景 |
num |
int 和 double 的父类 |
接受整数和浮点数的通用类型 |
BigInt |
任意大小整数 | 用于超大整数计算或加密等场景 |
2. 数字变量示例
dart
int a = 100;
double b = 3.1415;
num c = 50; // 也可以赋 int 或 double
num d = 2.718;
BigInt big = BigInt.parse('123456789012345678901234567890');
3. 数字运算示例
dart
int x = 10;
double y = 3.5;
var sum = x + y; // 13.5,结果是 double
var div1 = x / 3; // 3.3333 double
var div2 = x ~/ 3; // 3,整数除法,舍弃小数部分
print(x.isEven); // true
print(y.round()); // 4 四舍五入
print(y.floor()); // 3 向下取整
print(y.ceil()); // 4 向上取整
4. 类型转换
int 与 double 互转
dart
int i = 5;
double d = i.toDouble(); // int -> double
double e = 3.7;
int j = e.toInt(); // double -> int(直接截断)
int k = e.round(); // 四舍五入
int l = e.floor(); // 向下取整
int m = e.ceil(); // 向上取整
BigInt 与 int 互转
dart
BigInt bigInt = BigInt.from(123456789);
int normalInt = bigInt.toInt(); // 可能溢出,慎用
5. 字符串与数字的转换
字符串转数字
dart
int x = int.parse('123');
double y = double.parse('3.14');
int? maybeInt = int.tryParse('abc'); // 解析失败返回 null
数字转字符串
dart
String s1 = 123.toString(); // "123"
String s2 = 3.14159.toStringAsFixed(2); // "3.14" 保留两位小数
String s3 = 255.toRadixString(16); // "ff" 转16进制字符串
6. 其它常用小技巧
- 使用
num
类型时要注意,部分方法不同,如~/
只能用于整数类型。 BigInt
不能直接与int
、double
混合运算,需转换后再操作。- 用
tryParse
可避免因格式错误导致程序异常崩溃。
7. 总结
操作 | 关键函数/方法 | 备注 |
---|---|---|
整数与浮点转换 | toDouble() , toInt() , round() |
常用转换方式 |
字符串转数字 | int.parse() , double.parse() , tryParse() |
容错推荐用 tryParse |
数字转字符串 | toString() , toStringAsFixed() , toRadixString() |
格式化输出 |
大整数处理 | BigInt.parse() , BigInt.from() |
用于大数及加密计算 |
Flutter之Dart常用数据类型(字符串)
1. Dart 中字符串的基本介绍
-
类型 :
String
,表示一串 UTF-16 编码的字符序列。 -
特点:
- 不可变(immutable),即字符串内容不可更改,修改会生成新字符串。
- 支持单引号
'...'
或双引号"..."
定义。 - 支持多行字符串,使用三引号
'''...'''
或"""..."""
。
2. 字符串声明示例
dart
String s1 = 'Hello, Dart!';
String s2 = "Hello, Flutter!";
String s3 = '''这是
一个多行
字符串''';
String s4 = """另一种
多行
字符串""";
3. 字符串拼接
dart
String a = 'Hello';
String b = 'World';
String c = a + ' ' + b; // 使用 + 连接
String d = '$a $b'; // 使用字符串插值
String e = '${a.toUpperCase()} $b'; // 表达式插值
4. 字符串常用操作方法
方法 | 说明 | 示例 |
---|---|---|
length |
字符串长度 | 'abc'.length // 3 |
isEmpty / isNotEmpty |
是否为空 | ''.isEmpty // true |
toUpperCase() |
转大写 | 'abc'.toUpperCase() // 'ABC' |
toLowerCase() |
转小写 | 'ABC'.toLowerCase() // 'abc' |
contains() |
是否包含子串 | 'hello'.contains('ll') // true |
startsWith() |
是否以指定字符串开头 | 'hello'.startsWith('he') // true |
endsWith() |
是否以指定字符串结尾 | 'hello'.endsWith('lo') // true |
substring(start, [end]) |
截取子串 | 'hello'.substring(1,4) // 'ell' |
indexOf() |
查找子串第一次出现的位置 | 'hello'.indexOf('l') // 2 |
lastIndexOf() |
查找子串最后一次出现的位置 | 'hello'.lastIndexOf('l') // 3 |
replaceAll() |
替换所有匹配子串 | 'hello'.replaceAll('l', 'x') // 'hexxo' |
split() |
按分隔符拆分字符串成列表 | 'a,b,c'.split(',') // ['a','b','c'] |
trim() |
去除首尾空白 | ' hello '.trim() // 'hello' |
5. 字符串插值与表达式
dart
String name = 'Alice';
int age = 30;
String greeting = 'Hello, $name. You are $age years old.';
String complex = 'Next year you will be ${age + 1} years old.';
6. 多行字符串和原始字符串(Raw String)
- 多行字符串:
dart
String multiLine = '''
这是一个
多行字符串
示例。
''';
- 原始字符串(不转义 ``):
dart
String raw = r'In a raw string, \n is not special.';
7. 常用转义字符
转义符 | 说明 | 示例 |
---|---|---|
' |
单引号 | 'It's OK' |
" |
双引号 | "She said "Hi"" |
\ |
反斜杠 | 'C:\Path' |
\n |
换行 | 'Line1\nLine2' |
\t |
制表符 | 'Column1\tColumn2' |
8. 字符串编码与解码(简单示例)
dart
import 'dart:convert';
String text = 'Hello, Dart!';
List<int> utf8Bytes = utf8.encode(text); // 编码为 UTF8 字节
String decoded = utf8.decode(utf8Bytes); // 解码回字符串
9. 字符串和字符
- Dart 没有专门的
char
类型,单个字符用字符串的单字符表示。
dart
String char = 'A'; // 单字符字符串
print(char.length); // 1
10. 示例:统计字符串中某字符出现次数
dart
String str = "banana";
int count = str.split('a').length - 1; // 计算 'a' 出现次数
print(count); // 输出 3
Flutter之Dart常用数据类型(布尔、List)
1. 布尔类型(bool)
基础介绍
- Dart 中布尔类型用
bool
表示,只有两个值:true
和false
。 - 常用于条件判断、循环控制、开关标志等。
示例
dart
bool isActive = true;
bool isDone = false;
if (isActive) {
print('激活状态');
} else {
print('未激活');
}
常用逻辑运算符
运算符 | 说明 | 示例 |
---|---|---|
! |
取反(NOT) | !isActive |
&& |
逻辑与(AND) | isActive && isDone |
` | ` |
2. 列表类型(List)
基础介绍
List
是 Dart 中的有序集合,类似数组。- 可以存储相同类型或混合类型的元素(如果未指定泛型)。
- 支持动态长度,也可以声明为固定长度。
声明方式
dart
// 泛型指定为int
List<int> numbers = [1, 2, 3, 4];
// 类型自动推断
var fruits = ['apple', 'banana', 'orange'];
// 空列表
List<String> emptyList = [];
// 固定长度列表
var fixedList = List.filled(3, 0); // 长度3,初始值0
常用操作
dart
var list = [10, 20, 30];
// 访问元素
print(list[0]); // 10
// 修改元素
list[1] = 25;
// 添加元素
list.add(40);
// 插入元素
list.insert(1, 15);
// 删除元素
list.remove(30);
list.removeAt(0);
// 列表长度
print(list.length);
// 遍历列表
for (var item in list) {
print(item);
}
常用方法总结
方法 | 说明 |
---|---|
add(value) |
在末尾添加元素 |
insert(index, value) |
在指定位置插入元素 |
remove(value) |
删除首次出现的指定元素 |
removeAt(index) |
删除指定位置元素 |
clear() |
清空列表 |
contains(value) |
判断是否包含指定元素 |
indexOf(value) |
查找元素首次出现的索引 |
sort() |
对列表排序 |
reversed |
返回元素反转后的 Iterable |
3. 结合示例
dart
bool isLoggedIn = false;
List<String> messages = [];
if (!isLoggedIn) {
messages.add('请先登录');
}
messages.add('欢迎回来!');
for (var msg in messages) {
print(msg);
}
Flutter之Dart常用数据类型(Map)
1. Map 基础介绍
- Map 是键值对(key-value)集合,也称为字典或哈希表。
- 每个键对应一个值,键不允许重复,值可以重复。
- 键和值的类型可以是任意类型,但通常键是
String
或基本类型。 - Dart 的 Map 是无序的,若需要有序的 Map 可用
LinkedHashMap
。
2. Map 声明与初始化
dart
// 空 Map,键和值都为 String
Map<String, String> capitals = {};
// 直接初始化
var countries = {
'USA': 'Washington, D.C.',
'Japan': 'Tokyo',
'China': 'Beijing',
};
// 使用构造函数创建空 Map
var emptyMap = Map<String, int>();
// 也可以用 var,类型由内容推断
var scores = {'Alice': 90, 'Bob': 85, 'Cindy': 92};
3. Map 常用操作
dart
var map = {'apple': 3, 'banana': 5};
// 访问
print(map['apple']); // 3
print(map['orange']); // null,如果key不存在
// 修改或添加
map['apple'] = 4; // 修改
map['orange'] = 7; // 新增
// 删除
map.remove('banana');
// 判断是否包含key或value
print(map.containsKey('apple')); // true
print(map.containsValue(7)); // true
// 获取所有key和value
print(map.keys); // (apple, orange)
print(map.values); // (4, 7)
// 清空
map.clear();
4. 遍历 Map
方式一:遍历键值对
dart
var map = {'a': 1, 'b': 2};
map.forEach((key, value) {
print('$key -> $value');
});
方式二:遍历 key 或 value 列表
dart
for (var key in map.keys) {
print('Key: $key');
}
for (var value in map.values) {
print('Value: $value');
}
5. Map 与 JSON 互转(常见场景)
Flutter 常用 Map<String, dynamic>
来表示 JSON 对象。
dart
import 'dart:convert';
String jsonString = '{"name":"Alice","age":25}';
// JSON字符串转Map
Map<String, dynamic> user = json.decode(jsonString);
// Map转JSON字符串
String jsonStr = json.encode(user);
print(user['name']); // Alice
6. 总结
操作 | 方法/语法 | 说明 |
---|---|---|
创建 Map | {} 、Map() |
声明空或初始化 Map |
访问元素 | map[key] |
通过 key 访问值 |
添加/修改元素 | map[key] = value |
新增或修改元素 |
删除元素 | map.remove(key) |
删除指定 key 的元素 |
判断包含 | containsKey(key) |
是否存在指定 key |
遍历 | forEach() , for...in |
遍历 key-value 或 keys/values |
dynamic、var、Object的使用与区别
1. var
-
含义:由编译器根据赋值自动推断变量类型。
-
特点:
- 类型一旦推断确定,变量类型固定,后续赋值必须是相同类型。
- 不能重新赋不同类型的值,否则编译错误。
示例
dart
var x = 10; // x 是 int 类型
x = 20; // 合法
// x = 'hello'; // 编译错误,不能赋值 String 给 int 类型的变量
- 如果没有初始化,
var
就不能推断类型,必须显式声明类型:
dart
var y; // 编译错误,必须初始化或指定类型
int y; // 正确,声明未初始化的 int 变量
2. dynamic
-
含义:动态类型,关闭类型检查。
-
特点:
- 赋值后变量类型可以随时改变。
- 编译时不检查调用的方法和属性,运行时才会报错(类似弱类型语言)。
- 灵活但风险较高,易出现运行时错误。
示例
dart
dynamic a = 10;
a = 'hello'; // 合法,动态类型可以改变
a.someUndefinedMethod(); // 编译不报错,运行时才会出错
3. Object
-
含义:所有 Dart 对象的基类(顶层父类)。
-
特点:
- 编译器会检查方法和属性调用是否合法(需类型转换)。
- 赋值可以是任何类型,但访问方法时需类型断言或转换。
- 比
dynamic
更安全,但比var
更通用。
示例
dart
Object obj = 10;
obj = 'hello';
// 不能直接调用字符串方法
// obj.length; // 编译错误
// 需先转换类型
if (obj is String) {
print(obj.length); // 合法
}
4. 三者对比总结
特性 | var | dynamic | Object |
---|---|---|---|
类型推断 | 编译时根据初始值推断固定类型 | 动态类型,无类型检查 | 编译时类型为 Object |
类型安全 | 类型安全,后续赋值必须相同类型 | 不安全,任何类型均可赋值,调用不报错 | 相对安全,调用时需类型转换 |
访问成员 | 可调用变量实际类型的方法和属性 | 任何方法都可调用,运行时可能异常 | 只能调用 Object 定义的方法,需强转访问 |
赋值 | 必须符合推断类型 | 可随意更改类型 | 可赋任何类型,但限制访问 |
总结:
dynamic
和Object
最大的区别是:
dynamic
关闭了静态类型检查,Object
则有静态类型检查。
5. 适用场景
类型 | 使用场景 |
---|---|
var |
普通变量声明,类型已知且确定,推荐使用 |
dynamic |
需要完全动态、灵活的场景,如解析未知结构的数据、反射等 |
Object |
需要通用类型但保持一定安全性时,作为基类引用使用 |
6. 示例代码综合比较
dart
void main() {
var x = 10;
// x = 'hello'; // 编译错误
dynamic y = 10;
y = 'hello'; // 合法
// y.noSuchMethod(); // 运行时错误,编译不报错
Object z = 10;
z = 'hello';
// print(z.length); // 编译错误,需转换
if (z is String) {
print(z.length); // 合法,输出5
}
}
Flutter中常用的Dart方法类型
方法的构成:返回值类型 + 方法名 + 参数
-
返回值类型
- 可以省略,默认是
void
,也可以是具体类型如int
、String
等。为了规范,一般有具体返回值的都需要写
- 可以省略,默认是
-
方法名
- 匿名方法不需要方法名,这时调用时会用变量名或上下文来引用。
-
参数
- 包含参数类型和参数名。
- 参数类型可省略,Dart 允许类型推断。
- 参数可以是必选参数,也可以是可选参数(带默认值)。
- 参数支持位置可选参数和命名可选参数。
1. 入口方法(main)
dart
void main() {
print('程序入口');
}
2. 实例方法
dart
class Person {
String name;
Person(this.name);
void greet() {
print('Hello, $name');
}
}
var p = Person('Alice');
p.greet(); // Hello, Alice
3. 私有方法
前边加下划线表示私有,用于方法、变量等。
dart
class Example {
void publicMethod() {
_privateMethod();
}
void _privateMethod() {
print('这是私有方法,只能在本库访问');
}
}
var e = Example();
e.publicMethod(); // 可以调用私有方法间接访问
// e._privateMethod(); // 外部调用会报错
4. 匿名方法
dart
var list = [1, 2, 3];
list.forEach((item) {
print(item * 2);
});
// 输出 2,4,6
5. 静态方法
dart
class MathUtil {
static int add(int a, int b) => a + b;
}
print(MathUtil.add(3, 4)); // 7
6. 构造方法
dart
class Point {
int x, y;
Point(this.x, this.y); // 构造方法
}
var pt = Point(10, 20);
print('${pt.x}, ${pt.y}'); // 10, 20
7. setters 和 getters
dart
class Rectangle {
double _width = 0, _height = 0;
double get area => _width * _height;
set width(double w) => _width = w;
set height(double h) => _height = h;
}
var rect = Rectangle();
rect.width = 5;
rect.height = 10;
print(rect.area); // 50
8. 抽象方法
dart
abstract class Animal {
void speak(); // 抽象方法,子类必须实现
}
class Dog extends Animal {
@override
void speak() {
print('汪汪');
}
}
var dog = Dog();
dog.speak(); // 汪汪
9. 泛型方法
dart
T echo<T>(T value) {
return value;
}
print(echo<int>(123)); // 123
print(echo<String>('Hi')); // Hi
Flutter中的面向对象(标准构造方法、初始化列表与命名构造方法)
1. 标准构造方法(Default Constructor)
- 最常用的构造函数,直接用类名定义。
- 可带参数,常用于初始化对象属性。
示例
dart
class Person {
String name;
int age;
// 标准构造方法
Person(this.name, this.age);
}
var p = Person('Alice', 30);
print(p.name); // Alice
2. 初始化列表(Initializer List)
- 用于在构造函数体执行前初始化字段。
- 可以做一些复杂的计算、断言等。
- 初始化列表在冒号
:
后,构造函数体{}
前。
示例
dart
class Point {
final int x;
final int y;
final double distance;
// 初始化列表计算 distance
Point(this.x, this.y)
: distance = (x * x + y * y).toDouble().sqrt();
}
var pt = Point(3, 4);
print(pt.distance); // 5.0
注意:
Dart 标准库里 sqrt()
在 dart:math
里,用法如下:
dart
import 'dart:math';
Point(this.x, this.y) : distance = sqrt(x * x + y * y);
3. 命名构造方法(Named Constructor)
-
语法:
ClassName.constructorName(...)
,前面不 加factory
。 -
在初始化列表里或构造体中,直接给
final
字段赋值。 -
只能返回自身的新实例。
-
补充 :除了普通命名构造,也可以写重定向构造:
dartPoint.zero() : this(0, 0);
示例
dart
class Person {
String name;
int age;
// 标准构造方法
Person(this.name, this.age);
// 命名构造方法,默认年龄
Person.withNameOnly(this.name) : age = 18;
// 命名构造方法,工厂构造
Person.guest() {
name = 'Guest';
age = 0;
}
}
var p1 = Person('Alice', 30);
var p2 = Person.withNameOnly('Bob');
var p3 = Person.guest();
print(p2.age); // 18
print(p3.name); // Guest
4. 工厂构造方法
类似于java中的单例实现。
-
严格说,这也是 factory 构造,只不过常见用例是单例模式:
dartclass Logger { factory Logger() => _instance ??= Logger._(); static Logger? _instance; Logger._(); }
-
但它的能力不止单例:
- 缓存已有对象
- 抽象类返回具体子类
- JSON、数据库等场景下的"解析工厂"
-
补充 :任何带
factory
关键字的构造都属于"工厂构造",命名或未命名都行。
dart
class Logger {
static final Map<String, Logger> _cache = {};
final String name;
// 工厂构造方法,返回缓存实例
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name]!;
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
print('[$name] $msg');
}
}
var logger1 = Logger('UI');
var logger2 = Logger('UI');
print(logger1 == logger2); // true,返回同一个实例
5. 命名工厂构造方法
-
语法:在命名构造方法前加
factory
,形如factory ClassName.name(...)
。 -
不能 使用初始化列表(因此不能直接给
final
字段赋值),只能在函数体里return
一个实例。 -
可返回已缓存的实例、子类实例,甚至在抽象类中返回具体实现。
-
补充 :factory 构造方法不能声明为
const
。
dart
class Shape {
factory Shape.circle(double radius) => Circle(radius);
factory Shape.square(double size) => Square(size);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
class Square implements Shape {
final double size;
Square(this.size);
}
var circle = Shape.circle(5);
var square = Shape.square(10);
// 命名工厂构造方法返回不同子类
class Shape {
factory Shape.circle(double radius) => Circle(radius);
factory Shape.square(double size) => Square(size);
}
总结
关键点 | 说明 |
---|---|
标准构造方法 | 类名作为函数名,简单初始化字段 |
初始化列表 | 在构造体执行前初始化字段或计算属性 |
命名构造方法 | 多个构造方法,提供灵活的初始化途径,返回值 自身新实例 可以为const,常用于 参数多样的初始化 |
工厂构造方法 | 使用 factory 关键字声明,不一定返回新实例,可返回已有实例,常用于缓存和控制实例创建 |
命名工厂构造方法 | 带名字的工厂构造方法,支持多种工厂创建方案,封装复杂初始化逻辑,返回多种类型对象,可返回已有实例、子类、null,不能是const,缓存/单例/多态工厂/抽象工厂 |
Dart的get和set方法
在 Dart 中,get
和 set
关键字用来定义属性访问器,让我们可以像访问字段一样调用方法,同时还可以在读取或写入时添加自定义逻辑。l类似于java中的getter与setter。
1. 基本语法
dart
class Person {
String _name; // 私有字段,下划线表示私有
// Getter:无参数,返回类型可省略或写具体类型
String get name => _name;
// Setter:单参数,返回值必须是 void
set name(String value) {
_name = value;
}
Person(this._name);
}
使用方式:
dart
var p = Person('Alice');
print(p.name); // 调用 getter,输出 "Alice"
p.name = 'Bob'; // 调用 setter
print(p.name); // 输出 "Bob"
2. 只读或只写属性
- 只读 :只定义
get
,不提供set
(外部无法赋值) - 只写 :只定义
set
,不提供get
(外部无法读取)
dart
class Counter {
int _count = 0;
// 只读
int get count => _count;
// 只写
set increment(int delta) {
_count += delta;
}
}
var c = Counter();
print(c.count); // 0
c.increment = 5; // 相当于调用 setter,_count += 5
print(c.count); // 5
// c.count = 10; // 编译错误:没有对应 setter
// print(c.increment); // 编译错误:没有对应 getter
3. 带验证或计算逻辑
dart
class Rectangle {
double _width, _height;
Rectangle(this._width, this._height);
// 计算属性:无需存字段,每次都从宽高算
double get area => _width * _height;
// 带验证的 setter:宽度必须>0
set width(double w) {
if (w <= 0) throw ArgumentError('宽度必须大于0');
_width = w;
}
set height(double h) {
if (h <= 0) throw ArgumentError('高度必须大于0');
_height = h;
}
}
4. 顶层(或静态)getter/setter
除了类内部,你也可以在最外层定义顶层属性访问:
dart
int _globalCache = 0;
int get globalCache => _globalCache;
set globalCache(int v) {
_globalCache = v;
}
或者在类里定义静态访问器:
dart
class Config {
static String _env = 'prod';
static String get env => _env;
static set env(String e) {
_env = e;
}
}
5. 注意事项
get
必须返回值,不能有参数;set
必须是void
且只接受一个参数。- 属性访问器在外看起来就像字段,调用时无需加
()
。 - 如果没有显式定义 getter 或 setter,Dart 会自动为公有字段生成默认的访问器。
小结
get foo => ...
:定义一个可读属性set foo(value) { ... }
:定义一个可写属性- 可用于封装私有字段、添加验证逻辑、计算属性、顶层/静态配置等。
Dart的抽象类和方法
抽象类(abstract class
)
-
定义方式 :在类名前加
abstract
,例如abstract class Study
。 -
内容:可以包含
- 抽象方法(无方法体,需要子类实现)
- 具体方法(带方法体,可直接复用或重写)
-
特性:
- 不能被直接实例化
- 用于定义接口并提供部分实现
抽象方法
-
定义方式 :在抽象类中声明方法但不写方法体,例如
dartabstract class Study { void study(); // 抽象方法 }
-
关键点:
- 无需 也不允许 加
abstract
关键字修饰 - 子类必须实现所有抽象方法,否则编译失败
- 可以存在多个抽象方法,提供接口契约
- 无需 也不允许 加
抽象类的作用
- 替代接口 :Dart 无独立
interface
,抽象类既能定义契约,也能提供默认实现 - 模板方法:把共通行为写在抽象类,变化部分留给子类
- 代码组织:通过继承保持一致性,通过重写实现可扩展性
实现(继承)抽象类
-
继承方式:
dartclass StudyDart extends Study { @override void study() { /* 实现抽象方法 */ } }
-
须实现 :抽象类中声明的所有抽象方法
-
可选重写:抽象类中已有具体实现的方法,可选择重写或直接复用
实例化
- 抽象类 :不能
new Study()
- 子类:只要实现完抽象方法,就可被实例化
特性 | 抽象类 | 抽象方法 |
---|---|---|
定义关键字 | abstract class ClassName { ... } |
在抽象类中声明、不写方法体 |
方法体 | 可包含具体实现方法 | 不允许方法体 |
实例化 | 不能被实例化 | ------ |
子类要求 | 必须实现所有抽象方法,或自身也标记为 abstract |
子类必须重写,否则编译报错 |
主要用途 | 定义接口+可选默认实现(替代接口) | 强制子类提供特定行为 |
在Flutter中使用mixin
Mixin 的定义与特征 当我们想 "给任意类加一项新能力" ,又不希望因为这项能力去改变它们的继承层级,就非常适合用 Mixin。
- 代码复用工具:可以把一组方法和属性"混入"多个类,避免多重继承的复杂性。
- 必须继承自
Object
:不能extends
其他类,也无法调用super
。 - 无构造函数 :不能定义构造方法,也不能在 mixin 里调用
super()
。
如何创建与使用 Mixin
-
定义(Dart 3.0+ 必须)
dartmixin StudyMixin { void study2() { print('Mixin: study2 方法被调用'); } }
-
命名规范
- 推荐以
Mixin
为后缀,比如StudyMixin
、LoggerMixin
,便于识别。
- 推荐以
-
使用
dartclass Person { final String name; Person(this.name); } // Test 继承自 Person,并"with" StudyMixin class Test extends Person with StudyMixin { Test(String name): super(name); void demo() { print('$name 开始 Demo'); study2(); // 调用 mixin 方法 } }
完整示例
dart
// 1. 定义一个 Mixin
mixin StudyMixin {
void study2() {
print('StudyMixin → study2() 被调用');
}
}
// 2. 基础类
class Person {
final String name;
Person(this.name);
}
// 3. 混入 Mixin 的子类
class Test extends Person with StudyMixin {
Test(String name) : super(name);
void demo() {
print('$name: demo 方法开始');
study2(); // Mixin 提供的方法
}
}
void main() {
final t = Test('Alice');
t.demo();
// 输出:
// Alice: demo 方法开始
// StudyMixin → study2() 被调用
}
注意事项
- Dart 3.0+ 必须 用
mixin
关键字定义。 - Mixin 中不能有构造函数,也不可调用
super()
。 - 如果需要对宿主类有类型限制,可使用
on
关键字做约束(例如mixin M on SomeBase
)。
Dart泛型在Flutter中的应用
1. 泛型概述
泛型(Generics)允许在类、接口或方法中使用类型参数,使代码在保持类型安全的前提下具备更高的复用性。Dart 中使用尖括号 <>
来声明类型参数。
2. 泛型类:Cache<T>
示例
dart
class Cache<T> {
final Map<T, T> _storage = {};
/// 设置缓存
void setItem(T key, T value) {
_storage[key] = value;
}
/// 获取缓存,若不存在则返回 null
T? getItem(T key) {
return _storage[key];
}
}
-
说明:
T
为类型参数,可以是任意类型。- 内部使用
Map<T, T>
存储键值对。 getItem
返回可空类型T?
,避免直接抛出异常。
3. 泛型方法示例
dart
/// 从列表中取出第一个元素
T? getFirst<T>(List<T> items) {
return items.isNotEmpty ? items.first : null;
}
-
说明:
- 方法前也可声明类型参数
<T>
,根据调用时传入的类型自动推断。
- 方法前也可声明类型参数
4. 实际应用:存储不同类型
dart
void main() {
// 存储字符串
final stringCache = Cache<String>();
stringCache.setItem('greet', 'hello');
print(stringCache.getItem('greet')); // hello
// 存储整数
final intCache = Cache<int>();
intCache.setItem(1, 100);
print(intCache.getItem(1)); // 100
// 泛型方法
print(getFirst<int>([10, 20, 30])); // 10
}
5. 高级应用:泛型约束与通用接口
-
泛型约束
限制类型参数必须是某个类的子类或实现某个接口。例如,只缓存
num
类型及其子类:dartclass NumCache<T extends num> { final Map<String, T> _map = {}; void set(String key, T value) => _map[key] = value; T? get(String key) => _map[key]; }
-
通用接口
定义一个仓储接口,并让具体类实现:
dartabstract class Repository<T> { T fetchById(String id); void save(T item); } class User { final String name; User(this.name); } class UserRepository implements Repository<User> { @override User fetchById(String id) => User('User-$id'); @override void save(User item) { // 保存逻辑 } }
有哪些可以用在Flutter上的编程技巧?
-
面向对象编程技巧:
- 封装、继承和多态是面向对象设计的关键概念。
- 推荐将代码组织成小的可复用模块或方法,以提高代码的可维护性和复用性,并建议单个方法的长度不超过100行。
- 使用"点"操作符来查看API文档,这有助于理解每个方法的具体功能。
-
Dart编程技巧:
- 安全调用(Null Safety) :使用
?.
操作符处理可能为空的对象,避免空指针异常。 - 默认值设置 :利用
??
运算符为可能为空的变量指定一个默认值。 - 简化判断逻辑:采用集合或数组的方式优化多个条件的判断流程,使得代码更加简洁高效。
- 安全调用(Null Safety) :使用