Dart之数据类型

发布时间:2026/7/27 8:14:31
Dart之数据类型 一、前言学习 Dart 数据类型时可以先想象一个超市。超市里有饮料、蔬菜、水果、零食、日用品。不同商品不能全部随便塞进同一个容器里鸡蛋需要防摔的盒子饮料需要瓶子散装糖果需要袋子冷冻食品需要冷柜。代码里的数据也是一样。数字、文字、真假、列表、集合、键值对、函数它们都有各自更适合的“容器”。这个容器在编程里就叫数据类型。数据类型不是为了增加记忆负担而是为了告诉 Dart这份数据是什么、能做什么操作、应该如何存储和检查。这一篇我们系统梳理 Dart 中常见的数据类型。二、一句话理解Dart 数据类型就是数据的分类规则它决定一个值能表示什么内容、能使用哪些方法、能参与哪些操作。简单说int适合整数。double适合小数。String适合文字。bool适合真假。List适合有顺序的一组数据。Set适合去重的一组数据。Map适合键值对数据。先知道每种类型解决什么问题再去记它的方法学习会轻很多。三、Dart 数据类型总览Dart 中的数据类型可以先分成四类分类常见类型主要作用基本数据类型num、int、double、bool、String、dynamic表示数字、真假、文本和动态值集合类型List、Set、Map存放一组数据特殊类型Function、enum、Symbol、typedef表示函数、枚举、符号和类型别名类型转换toString()、toInt()、toDouble()在不同类型之间转换这张表是本文的地图。接下来我们一类一类展开。四、基本数据类型基本数据类型是最常见、最基础的一组类型。写 Dart 时几乎每天都会用到。4.1 数字类型 numnum是 Dart 数字类型的父类型它下面有两个常见子类型int和double。void main() { num a 10; num b 12.5; print(a); // 10 print(a.runtimeType); // int print(b); // 12.5 print(b.runtimeType); // double }虽然变量声明的是num但 Dart 会根据具体值判断它实际是int还是double。可以这样理解num是数字的大箱子。int是整数小盒子。double是小数小盒子。num适合你只关心“这是数字”但暂时不关心它是整数还是小数的场景。4.2 整数类型 intint用于表示整数。void main() { int age 18; int count 100; print(age); print(count); }整数没有小数部分适合表示年龄、数量、页码、索引、次数等。Dart 里的int支持平台相关的整数范围。通常开发中不需要一开始就背范围但要知道如果数字特别大可以使用BigInt。void main() { BigInt bigNumber BigInt.parse(1200000213231241431212133322222223); print(bigNumber); print(bigNumber.runtimeType); }如果需要随机整数可以使用dart:math里的Random。import dart:math; void main() { Random random Random(); print(random.nextInt(100)); // 生成 0 到 99 的随机整数 }4.3 浮点数类型 doubledouble用于表示小数。void main() { double price 19.9; double score 98.5; print(price); print(score); }即使写成4如果变量类型是double输出时也会表现为小数形式。void main() { double value 4; print(value); // 4.0 print(value.runtimeType); // double }double还能表示一些特殊值void main() { print(double.infinity); // Infinity print(double.negativeInfinity); // -Infinity print(double.nan); // NaN }使用double时要知道一个重要点浮点数可能出现精度问题。void main() { double value 0.1 0.2; print(value); // 0.30000000000000004 }这不是 Dart 算错了而是浮点数在计算机中的表示方式决定的。涉及金额、精确小数时要格外谨慎。4.4 数字类型基本操作数字类型可以做加、减、乘、除、整除、取余。void main() { num a 4; int b 5; print(a b); // 9 print(a - b); // -1 print(a * b); // 20 print(a / b); // 0.8 print(a ~/ b); // 0 print(a % b); // 4 }几个容易忽略的点/的结果是double。~/是整除结果是整数。%是取余常用于判断是否整除。数字也可以做类型转换void main() { int a 4; double b 5.1; print(a.toDouble()); // 4.0 print(b.toInt()); // 5 }数字可以比较和检查void main() { int a 4; double b 7.8; print(a.compareTo(b)); // -1表示 a 小于 b print(a.isNegative); // false print(a.isEven); // true print(a.isOdd); // false }常用数字方法还有void main() { int a -10; num b 5.834; print(a.abs()); // 10 print(a.clamp(1, 5)); // 1 print(b.ceil()); // 6 print(b.floor()); // 5 print(b.round()); // 6 print(b.truncate()); // 5 }这些方法可以这样记abs()求绝对值。clamp()把数限制在一个范围内。ceil()向上取整。floor()向下取整。round()四舍五入。truncate()直接截断小数部分。4.5 布尔类型 boolbool只有两个值true和false。void main() { bool isLogin true; bool isVip false; print(isLogin); print(isVip); }它常用于条件判断。void main() { bool hasTicket true; if (hasTicket) { print(可以入场); } else { print(不能入场); } }可以把bool理解成代码里的开关开是true关是false。4.6 字符串类型 StringString用于表示文本。void main() { String name Dart; String language Flutter; print(name); print(language); }Dart 字符串可以用单引号也可以用双引号。字符串支持插值也就是把变量嵌入字符串中。void main() { String name Dart; print(Hello, $name); }如果表达式比较复杂可以使用${}。void main() { int a 3; int b 5; print(结果是${a b}); }多行字符串可以使用三个引号。void main() { String slogan I love Dart I love Flutter ; print(slogan); }要注意Dart 中的字符串是不可变的。所谓修改字符串本质上通常是创建一个新的字符串。4.7 动态类型 dynamicdynamic表示这个变量可以存放任意类型的值。void main() { dynamic value 5; print(value); value hello, Dart; print(value); }看起来很灵活但也有代价编译器不会帮你做严格类型检查错误可能留到运行时才暴露。void main() { dynamic value 10; value hello; print(value.toUpperCase()); }如果你很确定数据结构优先使用明确类型。dynamic更适合处理临时不确定的数据比如外部接口返回、动态 JSON、迁移旧代码等。五、集合类型集合类型用于存放一组数据。如果基本类型像一个个商品那么集合类型就像购物袋、收纳盒和标签柜。5.1 列表 ListList是有顺序的集合允许重复元素。void main() { Listint numbers [1, 2, 2, 5, 9]; ListString skills [Dart, Flutter, Android]; print(numbers); print(skills); }List的特点有顺序。可以通过下标访问。允许重复。适合表示一串有序数据。void main() { ListString names [Tom, Jerry, Lucy]; print(names[0]); // Tom names.add(Lily); print(names); }如果你关心“第几个元素是什么”通常应该先想到List。5.2 集合 SetSet是不重复的集合。void main() { Setint numbers {1, 2, 2, 2, 4, 5}; print(numbers); // {1, 2, 4, 5} }Set的特点不允许重复元素。适合去重。适合判断某个元素是否存在。void main() { SetString tags {Dart, Flutter, Dart}; print(tags); // {Dart, Flutter} print(tags.contains(Dart)); // true }如果你关心“有没有重复”“是否包含某个值”通常可以考虑Set。5.3 映射 MapMap是键值对集合。void main() { MapString, int scores { 小明: 5, 小红: 2, 小敏: 7, }; print(scores); }Map的特点由 key 和 value 组成。key 必须唯一。可以通过 key 快速找到 value。void main() { MapString, int scores { Tom: 90, Jerry: 85, }; print(scores[Tom]); // 90 scores[Lucy] 100; print(scores); }如果你关心“通过一个名字找到对应数据”通常应该想到Map。5.4 List、Set、Map 怎么选三种集合很容易混在一起可以这样判断类型适合场景关键特点List商品列表、消息列表、步骤列表有顺序、可重复Set标签去重、唯一 ID 集合不重复Map用户信息、配置项、成绩表key-value简单记法要顺序用List。要去重用Set。要通过 key 查值用Map。六、特殊类型特殊类型不是每天都像String、int那样频繁出现但它们在 Dart 里也很重要。6.1 函数类型 Function在 Dart 中函数也可以作为值传递。void hello() { print(hello, Dart!); } void main() { Function fn hello; fn(); }这意味着我们可以把函数存到变量里也可以把函数作为参数传递。更推荐的写法是尽量写清楚函数类型。void main() { int Function(int, int) add (int a, int b) { return a b; }; print(add(3, 5)); }明确函数类型之后参数和返回值都会更清楚。6.2 枚举类型 enumenum用于定义一组固定的命名常量。enum Color { red, green, yellow, blue, } void main() { Color myColor Color.yellow; print(myColor); }枚举适合表示有限选项比如颜色、状态、方向、订单状态。enum OrderStatus { pending, paid, shipped, finished, }相比直接使用字符串枚举更安全。String status paidd; // 拼错了也可能不容易发现 OrderStatus orderStatus OrderStatus.paid;6.3 符号类型 SymbolSymbol用于表示 Dart 代码中的标识符。void main() { Symbol symbol #hello; print(symbol); // Symbol(hello) }普通业务代码里Symbol不算高频它更多出现在反射、元编程等场景中。初学阶段不需要深挖它只要知道#hello表示一个符号不是字符串。6.4 类型别名 typedeftypedef用于给类型起一个别名。最简单的例子typedef MyInt int; void main() { MyInt value 10; print(value); print(value.runtimeType); }不过实际开发中typedef更常用于函数类型别名。typedef Calculate int Function(int a, int b); int add(int a, int b) { return a b; } void main() { Calculate fn add; print(fn(3, 5)); }这样写可以让复杂函数类型更容易阅读。七、类型转换类型转换就是把一种类型的数据变成另一种类型。比如把整数变成小数把数字变成字符串把字符串解析成数字。7.1 隐式转换某些情况下Dart 会自动完成类型转换。最常见的是把int赋值给double。void main() { double value 4; print(value); // 4.0 print(value.runtimeType); // double }这里4原本是整数但赋值给double变量时会转换成4.0。不过不要以为 Dart 会随便帮你转。Dart 仍然是强类型语言很多转换需要你明确写出来。7.2 显式转换显式转换就是开发者主动调用方法转换类型。void main() { int a 4; double b 4.5; print(a.toDouble()); // 4.0 String text a.toString(); print(text); // 4 print(text.runtimeType); // String print(b.toInt()); // 4 }常见转换方法方法作用toString()转成字符串toInt()转成整数toDouble()转成小数int.parse()把字符串解析成整数double.parse()把字符串解析成小数字符串转数字示例void main() { String ageText 18; String priceText 19.9; int age int.parse(ageText); double price double.parse(priceText); print(age); print(price); }如果字符串格式不正确解析会报错。更稳的方式是使用tryParse()。void main() { String text abc; int? value int.tryParse(text); print(value); // null }tryParse()解析失败时返回null比直接抛异常更适合处理用户输入。八、常见误区8.1 以为 dynamic 更方便就到处用dynamic的确灵活但它会减少类型检查。void main() { dynamic value 10; value true; value hello; }这段代码能运行但如果后续调用了不存在的方法错误可能到运行时才出现。建议是能明确类型就明确类型不确定时再使用dynamic。8.2 混淆 List、Set、Map这三个集合类型不是谁替代谁而是解决不同问题。List解决顺序问题。Set解决去重问题。Map解决映射查找问题。写代码前先问自己我需要顺序、唯一性还是通过 key 找 value8.3 忽略 double 精度问题浮点数不适合直接用于所有精确计算。void main() { print(0.1 0.2); // 0.30000000000000004 }如果涉及金额、计费、精确小数要考虑更稳妥的处理方式比如按分存整数或使用专门的高精度方案。8.4 把类型转换想得太随意不是所有值都能安全转换。void main() { int value int.parse(abc); // 会报错 }处理外部输入时优先使用tryParse()。void main() { int? value int.tryParse(abc); print(value); // null }九、总结这一篇我们系统梳理了 Dart 中的数据类型。可以把数据类型理解成数据的容器不同数据需要放进不同容器里容器决定了数据能做什么操作。最常见的基础类型包括num数字类型的父类型。int整数。double小数。bool真假。String文本。dynamic动态类型。集合类型包括List有顺序、可重复。Set不重复。Map键值对。特殊类型包括Function函数也是值。enum固定选项集合。Symbol符号。typedef类型别名。最后类型转换分为隐式转换和显式转换。写 Dart 时不要只看值本身还要关注它的类型。类型选对了后面的代码才会更清晰、更安全、更容易维护。