深入解析 Dart 数据类型:从基础到实战的完全指南

在构建任何健壮的应用程序时,理解数据的本质都是第一步。无论是开发一个简单的计数器,还是构建复杂的金融系统,我们都必须精确地处理数据。与我们在 C++ 或 Java 等其他静态语言中遇到的情况类似,Dart 语言中的每个变量都有一个明确的关联数据类型。这不仅有助于编译器捕获潜在的错误,还能确保我们的程序逻辑清晰且可预测。

在 Dart 中,数据类型不仅仅是存放数据的容器,它们定义了我们可以在应用程序中表示和操作的值的种类。通过正确使用这些类型,我们可以优化内存使用,提高代码的可维护性,并避免运行时崩溃。

在这篇文章中,我们将深入探讨 Dart 编程语言的核心数据类型。我们将通过大量的实战代码示例,详细分析每种类型的特性,并分享一些在实际开发中非常有用的最佳实践。

Dart 数据类型全景图

Dart 的类型系统非常强大且表达力强。为了让你对全局有一个清晰的认识,我们可以将 Dart 的主要数据类型归纳如下表所示。建议你先快速浏览一下,随后我们将逐一攻克它们。

数据类型

关键字

描述

n

数字

int, double, num, BigInt

用于表示数值,包括整数、浮点数以及任意精度的整数 字符串

String

表示一系列字符的序列,通常用于文本处理 布尔值

bool

只有两个值:true 和 false,用于逻辑判断 列表

List

有序的元素集合,类似于其他语言中的数组,允许重复 集合

Set

一组唯一的元素集合,无序且不允许重复 映射

Map

键值对集合,通过唯一的键来快速查找对应的值 符文

Runes

用于表示 Unicode 字符(注:现代 Dart 更推荐使用 characters 包) 符号

Symbol

代表标识符的符号,常用于反射或元数据操作 空值

Null

表示空值或缺失的值,Dart 的空安全机制的核心

接下来,让我们深入细节,看看如何在实战中使用它们。

1. 数字类型:处理数值的核心

在 Dart 中,数字类型是我们处理数学运算和数值计算的基础。Dart 提供了多种数值类型来满足不同的计算需求,从普通的整数计数到高精度的科学计算。

核心数值类型

我们可以将 Dart 中的数字分为以下几类:

  • int: 用于表示整数。在 Web 平台上,它的值可以是任意大小的整数(模拟);而在原生平台(如移动端或桌面端)上,它通常表示 64 位整数,范围在 $-2^{63}$ 到 $2^{63} – 1$ 之间。
  • double: 用于表示 64 位精度的浮点数(小数),符合 IEEE 754 标准。当你需要处理小数(如价格、坐标)时,这是你的首选。
  • num: 这是一个抽象类型。它是 int 和 double 的父类。如果你希望一个变量既可以存储整数又可以存储小数,可以使用 num。
  • BigInt: 用于处理非常大的整数,远远超出普通 int 的范围。当我们处理加密算法、哈希值或极大数量的统计时,它会非常有用。

实战演练:声明与使用

让我们通过代码来看看如何声明和使用这些数值类型。

#### 声明整数 (int)

// 声明一个普通的整数变量
int age = 25;

// Dart 2.12+ 引入了空安全
// 这里的 ‘int?‘ 表示 count 可以是一个整数,也可以是 null
int? count;

// 使用 ‘var‘ 关键字(类型推断)
// Dart 编译器会自动识别 2024 为整数
var year = 2024; 

// 尝试修改 age
age = 26; // 这是允许的
// age = 26.5; // 报错!类型 ‘double‘ 不能赋给类型 ‘int‘

#### 声明小数 (double)

// 显式声明一个双精度浮点数
double pi = 3.1415;

// 一个可空的 double 变量
double? percentage;

// 再次强调类型推断
var temperature = 36.6; // Dart 自动将其识别为 double

// 在 Dart 中,如果一个数字包含小数点,它就是 double
// 即使它是 10.0,它也是 double,而不是 int

#### 使用 num 实现灵活存储

当我们不确定具体的数值类型,或者需要兼容两者时,可以使用 num。这是 Dart 中一个很棒的设计,体现了面向对象的灵活性。

num value = 10; 
print("当前值: $value"); // 输出: 10 (类型推断为 int)

// 这是允许的,因为 num 同时支持 int 和 double
value = 10.5; 
print("修改后的值: $value"); // 输出: 10.5 (类型变为 double)

// 如果我们将 value 初始化为 double,它就不能再变回 int 吗?
// 是的,一旦变量被赋值,其具体类型(int 或 double)就固定了。
// 如果我们需要在运行时改变类型,num 是最佳选择。

#### 使用 BigInt 处理超大整数

在处理天文数字或加密密钥时,普通的 int 可能溢出,这时我们需要 BigInt

// 直接解析字符串创建 BigInt
var bigNumber = BigInt.parse(‘123456789012345678901234567890‘);

// 也可以使用位运算创建
var anotherBigNumber = BigInt.two.pow(100); // 2 的 100 次方

print("大数相加结果: ${bigNumber + anotherBigNumber}");

综合示例:数值运算

让我们看一个更完整的例子,演示数值之间的转换和运算。

void main() {
  // 1. 基础定义
  int num1 = 2;
  double num2 = 1.5;

  // 2. 打印原始值
  print("整数值: $num1");
  print("浮点数值: $num2");

  // 3. 执行加法运算
  // 注意:int + double 结果为 double(这是 Dart 的隐式转换规则)
  var sum = num1 + num2;
  print("两数之和: $sum");

  // 4. 字符串转数字 (常见的实际操作)
  String priceString = "99.8";
  
  // 使用 parse 方法将字符串转为 double
  // 注意:如果字符串格式不对,程序会抛出异常,后续我们会讲如何处理
  double price = double.parse(priceString);
  print("商品价格: $price");

  // 5. 类型转换与保留小数
  double gallons = 10.5;
  int liters = gallons.toInt(); // 直接截断小数
  print("转换后的整数值: $liters");

  // 使用 toStringAsFixed 保留小数位
  double currency = 123.45678;
  print("保留两位小数: ${currency.toStringAsFixed(2)}"); // 输出: 123.46
}

2. 字符串:处理文本的艺术

在几乎所有的应用程序中,字符串都是最常用的数据类型。Dart 中的字符串是一系列 UTF-16 编码的字符序列。我们可以使用单引号或双引号来创建字符串,这一点非常灵活。

字符串的声明与转义

// 使用单引号或双引号完全取决于你的喜好
String str1 = "Geeks for Geeks";
String str2 = ‘Dart is Awesome‘;

// 如果字符串中包含引号,我们需要使用反斜杠进行转义
String sentence = ‘It\‘s a wonderful day!‘;

字符串插值与拼接

在现代开发中,直接拼接字符串(使用 + 号)虽然直观,但在处理复杂逻辑时显得笨重。Dart 提供了强大的字符串插值功能,这能让你的代码更整洁。

void main() {
  String name = "Alex";
  int age = 30;

  // 使用 + 拼接 (老派做法,有时效率不如直接插值)
  String message = "Hello, " + name + ". You are " + age.toString() + " years old.";
  print(message);

  // 使用字符串插值 (推荐做法,Dart 的优雅之处)
  // 使用 $ 符号引用变量,使用 ${} 引用表达式
  String newMessage = "Hello, $name. Next year, you will be ${age + 1}.";
  print(newMessage);
}

多行字符串

当我们需要处理大段文本或 SQL 语句时,Dart 的多行字符串显得尤为方便。使用三个引号即可实现。

void main() {
  // 使用单引号的三引号或多引号的三引号均可
  String multiLineText = ‘‘‘
    这是一个多行字符串。
    它可以跨越多行,不需要使用 
。
    并且它还会保留空格和缩进。
  ‘‘‘;

  print(multiLineText);

  // 实际应用:写一个 SQL 查询
  String query = """
    SELECT id, name, email 
    FROM users 
    WHERE age > 18
    ORDER BY name DESC
  """;
}

3. 布尔值:逻辑控制的基石

布尔值极其简单,只有 INLINECODE6aed33a7 和 INLINECODEecaffab0 两个值。但不要小看它,它是所有逻辑判断、循环控制和流程分支的核心。

布尔值的声明与判断

在 Dart 中,不像 JavaScript 那样会自动将非零数字或非空字符串视为 INLINECODE958d9153。Dart 进行了严格的类型检查。INLINECODE0fb52183 语句中的条件必须明确是一个布尔表达式。

void main() {
  // 显式声明布尔值
  bool isValid = true;
  bool isComplete = false;

  // 使用布尔值进行条件判断
  if (isValid) {
    print("数据有效!");
  }

  // 1 表示真,0 表示假?
  // int number = 1;
  // if (number) { ... } // 在 Dart 中这是错误的!编译器会报错
  // 必须显式比较:
  int number = 1;
  if (number > 0) {
    print("数字大于0"); // 这是正确的写法
  }

  // 布尔值来源于比较运算
  String str1 = ‘Coding is ‘;
  String str2 = ‘Fun‘;
  bool areStringsEqual = (str1 == str2);
  
  print("字符串是否相等: $areStringsEqual");
}

实用见解:使用 isEmpty 进行检查

在实际开发中,检查字符串或集合是否为空是高频操作。我们应该尽量利用 Dart 内置的布尔属性,而不是自己写逻辑。

String name = "";

// 不推荐
if (name.length == 0) { 
  print("名字是空的");
}

// 推荐 (更直观)
if (name.isEmpty) {
  print("名字是空的");
}

4. 集合:List, Set 和 Map

在实际业务中,我们很少只处理单个数据,更多时候处理的是一组数据。Dart 提供了三种强大的集合类型。

List:有序的列表

List 类似于数组,它存储有序的元素。你可以通过索引(从 0 开始)来访问列表中的元素。在 Dart 中,List 既可以是固定长度的,也可以是可增长长度的。

void main() {
  // 定义一个字符串列表
  //  是泛型,表示这个列表只能放 String
  List fruits = ["Apple", "Banana", "Orange"];

  // 访问第一个元素
  print("第一个水果: ${fruits[0]}");

  // 添加新元素
  fruits.add("Grape");

  // 删除元素
  fruits.remove("Banana");

  // 使用 ... 扩展运算符 (Spread Operator) - Dart 的神技
  // 它可以将一个列表中的元素插入到另一个列表中
  List moreFruits = ["Pineapple", ...fruits];
  print(moreFruits);
}

Set:唯一性的保证

Set 是无序的集合,并且它最重要的特性是:元素唯一。如果你尝试向 Set 中添加一个已经存在的元素,操作会被忽略。这在处理去重场景时非常有用。

void main() {
  // 定义一个数字 Set
  var numbers = {1, 2, 3, 4, 4}; // 这里的重复 4 会被自动忽略

  print(numbers); // 输出: {1, 2, 3, 4}

  // 将 List 转换为 Set 以去除重复元素
  List inputList = ["A", "B", "A", "C", "B"];
  var uniqueItems = inputList.toSet();
  print("去重后的集合: $uniqueItems");
}

Map:键值对的映射

Map 用于存储键值对。字典中通过唯一索引找单词,Map 中通过唯一键找对应的值。它是构建配置对象、JSON 数据模型的最常用类型。

void main() {
  // 定义一个 Map,键是 String,值可以是任意类型
  Map user = {
    "name": "张三",
    "age": 28,
    "isAdmin": true
  };

  // 访问 Map 中的值
  print("用户名: ${user[‘name‘]}");

  // 更新值
  user[‘age‘] = 29;

  // 检查键是否存在
  if (user.containsKey(‘email‘)) {
    print("Email: ${user[‘email‘]}");
  } else {
    print("该用户未填写邮箱");
  }
}

5. 特殊类型:Runes, Symbols 和 Null

除了上述常用的类型,Dart 还有一些用于特定场景的类型。

Runes (Unicode)

虽然普通 String 可以处理大部分文本,但如果你需要处理 Emoji 表情符号、特殊的颜文字或复杂的 Unicode 字符(如 𐍈),你可能需要使用 Runes。不过在 Dart 3+ 中,官方引入了更方便的 characters 包来处理这些问题。

Symbol

Symbol 通常用于 Dart 的反射机制(通过 mirror 包),用于获取类、方法或变量的标识符。在普通的 Flutter 或业务开发中,我们很少直接使用它,但在构建框架或底层库时,它是不可或缺的。

// Symbol 的用法示例
Symbol className = #MyClass;
print(Symbol); // 输出: Symbol("MyClass")

Null:空安全

Null 类型表示“空”或“无”。Dart 的空安全机制要求我们在定义变量时明确指出该变量是否可以为 null,这极大地减少了运行时的空指针错误。

// 可空变量
String? nickname; // 默认值为 null

// 非空变量必须初始化
String name = "Dart";

// 空值合并运算符 ??
// 表示如果左边为空,则使用右边的值
String finalName = nickname ?? "访客"; 
print("欢迎你, $finalName"); // 输出: 欢迎你, 访客

总结与最佳实践

通过这篇文章,我们已经涵盖了 Dart 中所有的核心数据类型。掌握这些类型是成为熟练的 Dart/Flutter 开发者的基石。让我们回顾一下几个关键要点:

  • 类型推断:尽量使用 var 来简化代码,但在类型不明确或为了代码可读性时,显式声明类型是更好的选择。
  • 空安全:这是 Dart 的现代化特性。请充分利用 INLINECODE39a3ea85、INLINECODE030fcefe(非空断言,慎用)、??(空值合并)等操作符来写出更安全的代码。
  • 字符串处理:优先使用字符串插值($variable)而不是拼接操作符,这样代码更易读。
  • 集合的选择:如果你需要索引访问,用 INLINECODE6e338de5;如果你需要去重,用 INLINECODEc1bc6f70;如果你需要键值对查找,用 Map

你现在可以尝试在本地编写这些代码,或者在实际项目中应用这些数据类型的技巧。熟练掌握它们,将让你的 Dart 代码更加健壮、优雅且高效。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/48462.html
点赞
0.00 平均评分 (0% 分数) - 0