4、Dart语言速通(上):变量与常量、内置类型、函数、操作符

好,咱们开始进入Dart语言的核心部分。说实话,很多初学者一上来就啃语法书,结果越看越晕。我的建议是——先动手,再理解。你写几行代码,跑起来,再回头看概念,会清晰很多。

这一章,咱们把Dart最常用的几个基础模块过一遍:变量、常量、内置类型、函数和操作符。这些都是你写Flutter时每天都要打交道的东西。

4.1 变量与常量:先搞清楚“能不能变”

Dart是强类型语言,但它的类型声明方式很灵活。我个人习惯是:能确定类型的,就显式声明;不确定的,用var让编译器去推。

var name = '张三';        // 自动推断为String
int age = 25;             // 显式声明
double height = 1.75;     // 浮点数
bool isStudent = true;    // 布尔值

这里有个坑——var一旦赋值,类型就锁死了。你不能把一个var name = '张三'后面再赋值为数字。我曾经在项目里见过有人这么写,结果编译直接报错。

注意:var不是动态类型,它只是让编译器帮你推断类型。一旦推断完成,类型就固定了。

再说常量。Dart里有两个关键字:finalconst。很多人搞混,其实很简单:

  • final:运行时赋值,赋值后不可改。适合从接口获取的数据。
  • const:编译时就确定值。适合固定配置。
final currentTime = DateTime.now();  // 运行时确定
const pi = 3.14159;                  // 编译时确定

嗯,这里要注意:const要求值在编译期就能算出来。你不能写const time = DateTime.now(),因为DateTime.now()是运行时才执行的。

4.2 内置类型:Dart给你准备好了哪些“积木”

Dart的内置类型不多,但够用。咱们挑几个重点说。

4.2.1 数字:int和double

Dart里没有float,只有intdouble。它们都继承自num。你想想看,这设计其实挺合理的——整数和小数,够用了。

int a = 10;
double b = 3.14;
num c = 42;   // 可以是int或double
小技巧:.round().toInt().toStringAsFixed(2)等方法做类型转换,很常用。

4.2.2 字符串:单引号还是双引号?

我个人习惯用单引号。除非字符串里本身有单引号,我才用双引号。Dart支持字符串插值,用$变量名${表达式}

var name = '李四';
print('你好,$name');           // 你好,李四
print('2 + 3 = ${2 + 3}');     // 2 + 3 = 5

多行字符串用三个引号:

var multiLine = '''
这是第一行
这是第二行
''';

4.2.3 布尔值:只有true和false

Dart的布尔值很严格。你不能像JavaScript那样写if (name)来判断非空。必须显式写if (name.isNotEmpty)。我第一次从JS转Dart时就被这个坑过。

避坑指南:Dart中只有true是true,其他都不是。0、空字符串、null都不会被隐式转换为false。

4.2.4 列表和映射:最常用的集合

列表就是数组,映射就是字典。写法很直观:

var list = [1, 2, 3];           // List<int>
var map = {'name': '王五', 'age': 30};  // Map<String, dynamic>

列表支持展开操作符...,这个在Flutter里拼Widget时特别好用:

var list1 = [1, 2];
var list2 = [0, ...list1, 3];  // [0, 1, 2, 3]

4.3 函数:Dart里函数是一等公民

这句话什么意思?说白了,函数可以像变量一样赋值、传参、返回。这在Flutter里太常见了——你写回调、事件处理,全是函数。

4.3.1 基本写法

int add(int a, int b) {
  return a + b;
}

如果函数体只有一行表达式,可以用箭头语法:

int add(int a, int b) => a + b;

我个人更喜欢箭头语法,简洁。但注意,箭头后面只能跟表达式,不能跟语句。

4.3.2 可选参数:位置参数和命名参数

Dart支持两种可选参数。我建议你用命名参数,可读性更好:

// 命名参数(推荐)
void greet({required String name, int age = 18}) {
  print('你好,$name,你$age岁了');
}

// 调用
greet(name: '赵六', age: 25);

required关键字表示这个参数必须传。不加required的就是可选参数,可以给默认值。

经验之谈:在Flutter里,几乎所有Widget的构造参数都是命名参数。你写Text('你好')其实传的是data参数,但它是位置参数的特例。

4.3.3 匿名函数:回调的基石

匿名函数没有名字,直接定义直接用。最常见的场景就是列表遍历:

var list = [1, 2, 3];
list.forEach((item) {
  print(item);
});

箭头写法更简洁:

list.forEach((item) => print(item));

4.4 操作符:别被“?? ”吓到

Dart的操作符大部分和其他语言一样。但有几个是Dart特有的,我重点说一下。

4.4.1 类型判定操作符

操作符 作用 示例
as 类型转换 obj as String
is 类型判断 obj is String
is! 类型否定判断 obj is! String

我建议你多用is,少用as。因为as如果类型不匹配会抛异常。用is先判断,更安全。

4.4.2 空安全操作符:?? 和 ??=

这是Dart 2.12引入的特性。说白了就是处理null的语法糖:

var name = null;
var displayName = name ?? '默认名称';  // 如果name为null,用默认值

name ??= '新名称';  // 如果name为null,才赋值

我曾经在项目里看到有人写一堆if (xxx != null)的嵌套判断,后来全改成??了,代码清爽很多。

4.4.3 级联操作符:..

这个操作符让你可以对同一个对象连续操作。在Flutter里配置Widget时特别好用:

var sb = StringBuffer()
  ..write('Hello')
  ..write(' ')
  ..write('World');

你想想看,如果没有..,你得写三行sb.write(...)。现在一行搞定。

4.5 小结:先跑起来再说

这一章的内容,说白了就是Dart的“ABC”。你不用全部记住,但要知道它们存在。我建议你打开DartPad,把上面的代码都敲一遍。遇到报错别慌,看看错误信息,改一改,跑通了,你就记住了。

下一章咱们继续聊Dart的面向对象特性——类、继承、接口这些。到时候你会发现,Flutter里到处都是类和对象。

核心要点回顾:
  • 变量用var或显式类型声明,常量用finalconst
  • 内置类型:int、double、String、bool、List、Map
  • 函数是一等公民,支持箭头语法和命名参数
  • 空安全操作符??和级联操作符..是Dart特色