Dart: Dart 变量与数据类型 — 五种声明方式与内置类型

变量是数据的容器,类型是容器的形状 — 选对容器,代码才安全。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:随意声明变量导致运行时崩溃

Bob 在开发 DataPipeline 的早期版本时,所有变量都用 var 声明,还经常中途修改变量类型。一次重构中,他误将 int orderCount 改为 String orderCount,编译没报错(dynamic 类型),但运行时 orderCount * price 抛出异常,导致处理 500,000 条订单的批处理任务中断,报表延迟 4 小时交付。

(2) 类型安全的解法

Dart 提供了从宽松到严格的一系列声明方式:const(编译时常量)→ final(运行时常量)→ var(推断类型)→ late(延迟初始化)→ dynamic(动态类型)。合理选用能让编译器帮你找 bug。

DART
// Best practice for DataPipeline configuration
const int maxRecords = 1000000;       // Compile-time constant
final String pipelineName;             // Runtime constant (set once)
var processedCount = 0;                // Inferred as int, mutable
late String outputPath;                // Initialized later
// dynamic should be avoided if possible
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. 五种声明方式

(1) 声明方式决策树

100%
graph LR
  subgraph Declarations
    A[var] --> B[Type inference]
    C[final] --> D[Runtime constant]
    E[const] --> F[Compile-time constant]
    G[late] --> H[Lazy initialization]
    I[dynamic] --> J[Dynamic type]
  end
  subgraph Built-in Types
    K[int] --- L[double]
    M[String] --- N[bool]
  end
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:var — 类型推断

DART
void main() {
  var name = 'DataPipeline';   // Inferred as String
  var count = 1000;            // Inferred as int
  var rate = 0.08;             // Inferred as double

  // Can reassign same type
  name = 'Analytics';          // OK - still String
  count = 2000;                // OK - still int

  // Cannot change type
  // count = 'two thousand';   // Compile error!
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:final — 运行时常量

DART
void main() {
  // Set once at runtime
  final DateTime startTime = DateTime.now();
  final String configPath = '/etc/datapipeline/config.yaml';

  // Cannot reassign
  // configPath = '/other/path';  // Compile error!

  // final with collection - contents can change
  final List``<String>`` sources = ['orders.csv', 'products.csv'];
  sources.add('customers.csv');   // OK - modifying contents
  // sources = ['new_list'];       // Error - cannot reassign reference
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:const — 编译时常量

DART
void main() {
  // Must be known at compile time
  const int maxRecords = 1000000;
  const double taxRate = 0.08;
  const String version = '1.0.0';

  // const collections are deeply immutable
  const List``<String>`` formats = ['json', 'csv', 'html'];
  // formats.add('xml');  // Error! Cannot modify const list

  // const constructor
  const config = PipelineConfig(batchSize: 10000);
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:late — 延迟初始化

DART
class DataPipeline {
  late String outputPath;  // Will be set later

  void configure(String path) {
    outputPath = path;
  }

  late final int maxRecords = _loadMaxRecords();  // Computed on first access

  int _loadMaxRecords() {
    print('Loading max records config...');
    return 1000000;
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:dynamic — 动态类型

DART
void main() {
  dynamic value = 'hello';   // String
  value = 42;                // OK - now int
  value = [1, 2, 3];        // OK - now List

  // No compile-time type checking - risky!
  // value.nonExistentMethod();  // No compile error, runtime crash

  // Prefer Object? over dynamic when you need flexibility
  Object? safeValue = 'test';
  // safeValue.nonExistentMethod();  // Compile error - safer
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) 五种声明方式对比

声明方式 类型 可变 初始化时机 安全性 使用场景
const 编译时确定 不可变 声明时 最高 配置常量、枚举值
final 运行时确定 不可变 声明时或构造函数 运行时配置、注入值
var 推断 可变 声明时 局部变量、计数器
late 显式标注 可变 延迟 延迟初始化字段
dynamic 运行时 可变 任意 JSON 解析、互操作

4. 内置类型详解

(1) 数值类型

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:int 与 double

DART
void main() {
  int orderCount = 1500;
  double totalAmount = 52500.75;
  num genericNum = 42;      // num is supertype of int and double
  genericNum = 3.14;        // OK - num accepts both

  // Arithmetic operations
  double avgOrderValue = totalAmount / orderCount;
  int roundedDown = totalAmount.toInt();

  // Convenient methods
  print(totalAmount.toStringAsFixed(2));  // 52500.75
  print(orderCount.isEven);              // false (1500 is even, true)
  print(orderCount.clamp(0, 1000));      // 1000 (clamped to max)
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
类型 范围 用途
int 64-bit (JS 限制时不同) 计数、索引
double 64-bit IEEE 754 金额、比例
num int + double 的父类型 通用数值

(2) String 类型

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:String 插值与操作

DART
void main() {
  // String interpolation
  String product = 'DataPipeline';
  int users = 5000;
  print('$product has $users users');               // Simple interpolation
  print('Revenue: ${users * 99} USD');              // Expression interpolation
  print('Average: ${(125000 / users).toStringAsFixed(2)} USD');

  // Multi-line strings
  String description = '''
  DataPipeline - E-Commerce Analytics Tool
  Processes up to 1,000,000 orders per batch
  Supports JSON, CSV, and HTML output formats
  ''';

  // Raw strings (no escape processing)
  String path = r'C:\Users\Bob\data\orders.csv';
  String regex = r'\d{4}-\d{2}-\d{2}';  // Date pattern

  // Useful methods
  String raw = '  Hello, World!  ';
  print(raw.trim());                  // 'Hello, World!'
  print(raw.toUpperCase());           // '  HELLO, WORLD!  '
  print('USD 1,500.00'.replaceAll(',', ''));  // 'USD 1500.00'
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
特性 语法 示例
简单插值 $variable '$name'
表达式插值 ${expr} '${a + b}'
多行字符串 '''...''' 三引号
原始字符串 r'...' r'\n' 保持原样

(3) bool 类型

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:布尔值与条件

DART
void main() {
  bool isProduction = true;
  bool isDebug = false;

  // Dart requires explicit bool in conditions
  int count = 0;
  // if (count) {}  // Error! Must be bool
  if (count > 0) {  // OK - explicit comparison
    print('Has records');
  }

  // Logical operators
  bool shouldProcess = isProduction && !isDebug;
  bool hasData = count > 0 || isProduction;
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

5. 类型推断与显式标注

(1) 最佳实践

DART
// Good - let the compiler infer for local variables
var name = 'DataPipeline';     // Inferred String
var count = 100;               // Inferred int
var items = ``<String>``[];        // Explicit generic, inferred List

// Good - explicit type for public API
String get displayName => name;
int get maxCapacity => 1000000;

// Good - final for values that don't change
final startTime = DateTime.now();

// Avoid - unnecessary explicit type on locals
// String name = 'DataPipeline';  // Redundant
// int count = 100;               // Redundant
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
场景 推荐 原因
局部变量 var / final 减少冗余,推断足够
公开 API 显式标注 文档清晰,接口稳定
集合泛型 显式泛型 避免推断为 `List`````
构造函数参数 显式标注 契约清晰

6. Bob 场景:DataPipeline 配置变量

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:配置变量设计

DART
// DataPipeline configuration constants
const String appName = 'DataPipeline';
const int defaultBatchSize = 10000;
const double defaultTaxRate = 0.08;

// Runtime configuration
class PipelineConfig {
  final String version;
  final int batchSize;
  final String outputFormat;

  PipelineConfig({
    this.version = '1.0.0',
    this.batchSize = defaultBatchSize,
    this.outputFormat = 'json',
  });

  @override
  String toString() =>
      '$appName v$version | Batch: $batchSize | Format: $outputFormat';
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

7. 完整示例:DataPipeline 变量声明实战

DART
// ============================================
// DataPipeline Variable Declaration Demo
// Shows proper use of all declaration types
// ============================================

// Compile-time constants - never change across runs
const String appName = 'DataPipeline';
const int maxRecords = 1000000;
const double taxRate = 0.08;
const List``<String>`` supportedFormats = ['json', 'csv', 'html'];

class Order {
  final String id;          // Runtime constant after construction
  final double amount;      // Runtime constant after construction
  late String status;       // Will be set by processing logic

  Order({required this.id, required this.amount});

  double get tax => amount * taxRate;
  double get total => amount + tax;

  String get formattedAmount =>
      '\$${amount.toStringAsFixed(2)} USD';
}

class DataPipeline {
  final String name;
  var processedCount = 0;              // Mutable counter
  late final DateTime startTime;       // Set on first run

  DataPipeline({required this.name});

  void start() {
    startTime = DateTime.now();
    print('$appName started at $startTime');
  }

  void processOrder(Order order) {
    processedCount++;
    order.status = 'processed';
    print('Order ${order.id}: ${order.formattedAmount} '
        '(tax: \$${order.tax.toStringAsFixed(2)} USD)');
  }

  void printSummary() {
    print('\n=== Summary ===');
    print('Pipeline: $name');
    print('Processed: $processedCount orders');
    print('Capacity: $processedCount / $maxRecords');
  }
}

void main() {
  final pipeline = DataPipeline(name: 'E-Commerce Analytics');
  pipeline.start();

  final orders = [
    Order(id: 'ORD-001', amount: 1500.00),
    Order(id: 'ORD-002', amount: 3250.50),
    Order(id: 'ORD-003', amount: 890.25),
  ];

  for (final order in orders) {
    pipeline.processOrder(order);
  }

  pipeline.printSummary();
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
DataPipeline started at 2024-01-15 10:30:00.000
Order ORD-001: $1500.00 USD (tax: $120.00 USD)
Order ORD-002: $3250.50 USD (tax: $260.04 USD)
Order ORD-003: $890.25 USD (tax: $71.22 USD)

=== Summary ===
Pipeline: E-Commerce Analytics
Processed: 3 orders
Capacity: 3 / 1000000

❓ 常见问题

Q:var 和 final 该用哪个? A:如果变量不会重新赋值,优先用 final。如果需要修改,用 var。Dart 官方 lint 规则 prefer_final_locals 推荐优先使用 final。

Q:const 和 final 的区别是什么? A:const 是编译时常量,值在编译时必须确定;final 是运行时常量,只能赋值一次但值可以在运行时计算。DateTime.now() 不能用 const。

Q:什么时候用 late? A:当变量无法在声明时初始化,但你能保证在使用前一定会初始化时用 late。常见场景:依赖注入、构造后配置。误用会导致运行时 LateInitializationError。

Q:dynamic 和 Object? 有什么区别? A:dynamic 关闭所有类型检查,编译器不校验任何方法调用;Object? 保留类型检查,只能调用 Object 的方法。优先用 Object?。

Q:Dart 的 int 在 Web 和 VM 上一样吗? A:不完全一样。Dart VM 上 int 是 64 位整数;编译为 JS 时,int 受 JS Number 限制(53 位精度)。大数运算需注意。

Q:String 可以用单引号还是双引号? A:都可以,效果完全一样。Dart 官方风格指南推荐单引号,除非字符串内包含单引号时才用双引号。

Q:num 类型什么时候用? A:当你需要同时接受 int 和 double 的参数或变量时用 num。num 是 int 和 double 的父类型,支持基本算术运算。


📖 小节


📝 作业

  1. 基础题(难度⭐):声明以下变量:一个 const 的应用名称、一个 final 的当前时间、一个 var 的计数器,分别打印它们的值和类型。
  2. 进阶题(难度⭐⭐):写一个函数,接受 num 类型参数,返回该数值的 USD 格式字符串(如 formatUSD(1500.5) 返回 "$1,500.50 USD")。注意 int 和 double 的处理。
  3. 挑战题(难度⭐⭐⭐):设计一个 Config 类,用 late 延迟初始化数据库连接字符串,用 const 定义默认值,用 final 保存从环境变量读取的配置,展示三种声明方式的协作。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏