AIGC标识 Dart之运算符与表达式

Dart之运算符与表达式思维导图

一、前言

刚开始学 Dart 时,很多人会先看到一堆符号:+-*/===&&??..

这些符号看起来很零散,好像只能靠背。其实不用这样理解。

你可以先把运算符想象成代码里的“工具按钮”:有的负责计算,有的负责赋值,有的负责比较,有的负责判断条件,还有的专门处理空值。

表达式则是这些工具组合出来的一句话。比如 3 + 5 是一个表达式,age >= 18 也是一个表达式,name ?? '游客' 也是一个表达式。

这一篇我们就用系统化的方式,把 Dart 中常见的运算符和表达式讲清楚。

二、一句话理解

运算符是 Dart 用来完成计算、赋值、比较、判断等操作的符号;表达式是由变量、值、运算符和函数调用组合而成,并且能产生结果的一段代码。

简单说:

  • 运算符:告诉代码“要做什么操作”。
  • 表达式:把操作组合起来,得到一个结果。
  • 学习重点:不要死背符号,要理解每类符号解决什么问题。

三、什么是运算符

运算符就是代码中的操作符号。

在生活中,我们会用不同工具处理不同事情:剪刀用来剪,胶水用来粘,计算器用来算数。代码里的运算符也是这样,不同符号负责不同操作。

先建立一张总表:

类型 常见符号 作用 常见结果
算术运算符 +-*/~/% 做数学计算 数值
赋值运算符 =+=-=*=/=~/=%= 给变量赋值或更新变量 更新变量
比较运算符 >>=<<===!= 比较两个值 bool
逻辑运算符 &&、` !`
自增自减 ++-- 快速加 1 或减 1 更新变量
空感知运算符 ????=?. 处理 null 取决于表达式
级联运算符 ..?.. 连续操作同一个对象 原对象
类型判断 isis! 判断对象类型 bool

这张表不需要一次背下来,先知道每类符号负责什么事情,后面写代码时就能逐步熟悉。

3.1 算术运算符

算术运算符用于完成基础数学计算。

void main() {
  print(3 + 5);  // 8
  print(3 - 5);  // -2
  print(3 * 5);  // 15
  print(3 / 5);  // 0.6
  print(3 ~/ 5); // 0
  print(3 % 5);  // 3
}

这里需要注意两个比较有 Dart 特点的符号:

  • /:普通除法,结果是 double
  • ~/:整除,只保留整数部分,结果是 int

% 是取余,它适合判断一个数能不能被另一个数整除。

void main() {
  print(10 % 2); // 0,说明 10 可以被 2 整除
  print(10 % 3); // 1,说明 10 除以 3 余 1
}

再看一个更贴近日常的例子:如果 10 个苹果平均分给 3 个人,每个人能拿几个,还剩几个?

void main() {
  int apples = 10;
  int people = 3;

  print(apples ~/ people); // 3,每个人分到 3 个
  print(apples % people);  // 1,还剩 1 个
}

所以 ~/% 经常成对出现:

  • ~/ 关心“完整分了几份”。
  • % 关心“分完还剩多少”。

3.2 赋值运算符

赋值运算符用于把右边的值交给左边的变量。

最常见的是 =

void main() {
  int count = 1;
  print(count); // 1
}

如果变量需要在原来的基础上继续计算,可以使用复合赋值运算符。

void main() {
  int count = 10;

  count += 2; // 等价于 count = count + 2
  print(count); // 12

  count -= 3;
  print(count); // 9

  count *= 2;
  print(count); // 18

  count ~/= 4;
  print(count); // 4
}

复合赋值的核心不是新语法多高级,而是让“基于原值更新”这件事写得更简洁。

常见复合赋值可以这样理解:

写法 等价写法 含义
a += b a = a + b 在原值上增加
a -= b a = a - b 在原值上减少
a *= b a = a * b 在原值上相乘
a /= b a = a / b 在原值上除以
a ~/= b a = a ~/ b 在原值上整除
a %= b a = a % b 在原值上取余

注意:/= 的结果通常是 double,所以左侧变量类型要能接住结果。

void main() {
  double price = 10;
  price /= 4;
  print(price); // 2.5
}

3.3 比较运算符

比较运算符用于比较两个值之间的关系,结果一定是 bool 类型。

void main() {
  int age = 18;

  print(age > 16);  // true
  print(age >= 18); // true
  print(age < 18);  // false
  print(age <= 20); // true
  print(age == 18); // true
  print(age != 18); // false
}

这里最容易混淆的是 ===

  • =:赋值,把右边交给左边。
  • ==:比较,判断左右是否相等。
void main() {
  int age = 18;       // 赋值
  print(age == 18);   // 比较
}

一个是“放进去”,一个是“看一样不一样”。

3.4 逻辑运算符

逻辑运算符用于组合多个布尔条件。

void main() {
  int age = 20;
  bool hasTicket = true;

  print(age >= 18 && hasTicket); // true
  print(age < 18 || hasTicket);  // true
  print(!hasTicket);             // false
}

三个常见逻辑运算符:

  • &&:并且,左右都为 true 才是 true
  • ||:或者,只要有一个为 true 就是 true
  • !:取反,把 truefalse,把 falsetrue

可以用生活场景理解:

  • 进电影院:成年人并且有票,才能进场。
  • 找交通方式:坐地铁或者打车,有一个可行就能出发。
  • 取反:不是会员,就走非会员逻辑。

逻辑运算符还有一个很重要的特点:短路。

&& 遇到左边为 false 时,右边不会再执行,因为整体已经不可能为 true

bool checkTicket() {
  print('检查门票');
  return true;
}

void main() {
  int age = 16;

  print(age >= 18 && checkTicket());
  // age >= 18 为 false,所以 checkTicket 不会执行
}

|| 遇到左边为 true 时,右边也不会再执行,因为整体已经确定为 true

bool useBackupPlan() {
  print('启动备用方案');
  return true;
}

void main() {
  bool primaryOk = true;

  print(primaryOk || useBackupPlan());
  // primaryOk 为 true,所以 useBackupPlan 不会执行
}

短路不是细节,它会影响函数是否被调用、日志是否打印、网络请求是否触发。写条件时要知道右侧表达式可能不会执行。

四、Dart 中比较特别的运算符

除了基础运算符,Dart 还有一些非常常用、也很有语言特色的运算符。

4.1 自增自减运算符

自增自减运算符用于让变量快速加 1 或减 1。

void main() {
  int i = 5;

  print(++i); // 6,先加 1,再使用
  print(i);   // 6

  print(i++); // 6,先使用,再加 1
  print(i);   // 7
}

前置和后置的区别是:

  • ++i:先变化,再使用。
  • i++:先使用,再变化。

如果只是单独写一行,差别不明显。

i++;
++i;

但如果放在 print()、赋值或表达式里,就要注意执行顺序。

自减也是同样规则:

void main() {
  int i = 5;

  print(--i); // 4,先减 1,再使用
  print(i--); // 4,先使用,再减 1
  print(i);   // 3
}

实际开发里,如果表达式变复杂,我更建议拆开写。

count++;
print(count);

这种写法比把 count++ 藏在复杂表达式里更容易读。

4.2 空感知运算符

空感知运算符用于处理 null

Dart 是空安全语言,很多时候我们需要明确告诉代码:如果这个值为空,应该怎么办。

void main() {
  String? name;

  String displayName = name ?? '游客';
  print(displayName); // 游客
}

?? 的意思是:如果左边不是 null,就用左边;如果左边是 null,就用右边。

再看 ??=

void main() {
  String? name;

  name ??= '默认用户';
  print(name); // 默认用户
}

??= 的意思是:如果变量当前是 null,才把右边的值赋给它。

还有 ?.

void main() {
  String? name;

  print(name?.length); // null
}

如果 namenull,就不会继续调用 length,而是直接返回 null

把三个空感知运算符放在一起看,会更清楚:

运算符 作用 是否会修改变量
?? 左边为空时使用右边 不会
??= 左边变量为空时才赋值
?. 左边为空时不继续访问成员 不会

例如用户昵称为空时显示默认文案:

void main() {
  String? nickName;

  print(nickName ?? '未设置昵称');
}

如果希望变量本身也被默认值填上,才使用 ??=

void main() {
  String? nickName;

  nickName ??= '未设置昵称';
  print(nickName);
}

如果访问一个可能为空的对象属性,使用 ?. 可以避免空对象继续调用成员。

class User {
  User(this.name);
  final String name;
}

void main() {
  User? user;
  print(user?.name); // null
}

4.3 级联运算符

级联运算符 .. 用于对同一个对象连续操作。

class User {
  String name = '';
  int age = 0;

  void sayHello() {
    print('Hello, $name');
  }
}

void main() {
  User user = User()
    ..name = 'Tom'
    ..age = 18
    ..sayHello();
}

如果不用级联运算符,写法通常是:

void main() {
  User user = User();
  user.name = 'Tom';
  user.age = 18;
  user.sayHello();
}

级联运算符的作用不是改变逻辑,而是让“连续设置同一个对象”这件事更集中。

Dart 还有空安全级联 ?..。当对象可能为空时,可以避免继续执行后续级联操作。

class User {
  String name = '';

  void sayHello() {
    print('Hello, $name');
  }
}

void main() {
  User? user;

  user
    ?..name = 'Tom'
    ..sayHello();
}

这里 usernull,所以后面的设置和方法调用不会继续执行。

使用级联时要注意:级联操作的目标始终是最开始的那个对象,而不是每一步调用的返回值。

4.4 类型判断运算符

类型判断运算符用于判断一个对象是不是某种类型。

void main() {
  Object value = 'hello';

  print(value is String);  // true
  print(value is! int);    // true
}
  • is:判断是不是某个类型。
  • is!:判断不是不是某个类型。

这在处理 Objectdynamic 或复杂数据时很常见。

void printValue(Object value) {
  if (value is String) {
    print(value.length);
  }
}

value is String 成立后,Dart 能更明确地知道它是字符串,就可以使用字符串相关能力。

这类能力也叫类型提升。简单说,就是 Dart 看到你已经判断过类型,就会在对应代码块里把变量当成更具体的类型使用。

void printLength(Object value) {
  if (value is String) {
    print(value.length);
  } else {
    print('不是字符串');
  }
}

如果判断不是某个类型,可以使用 is!

void main() {
  Object value = 100;

  if (value is! String) {
    print('value 不是字符串');
  }
}

当你从接口、JSON、动态数据里拿到不确定类型的值时,类型判断非常有用。

4.5 条件访问和非空断言

除了上面几类,Dart 空安全里还有一个很常见的符号:!

这里的 ! 不是逻辑取反,而是非空断言。

void main() {
  String? name = 'Tom';
  print(name!.length);
}

name! 的意思是:我确定 name 这里一定不是 null

但它有风险。如果实际运行时值是 null,程序会报错。

void main() {
  String? name;
  print(name!.length); // 运行时报错
}

所以初学阶段要记住:

  • 能用 ?.?? 安全处理时,不要急着用 !
  • ! 适合你非常确定某个值不为空的场景。

五、什么是表达式

表达式是能产生结果的一段代码。

一个值可以是表达式,一个变量可以是表达式,多个值和运算符组合起来也可以是表达式。

5.1 简单表达式

简单表达式通常由一个值、一个变量或一个很短的运算组成。

void main() {
  int age = 18;

  print(age);
  print(3 + 5);
  print(age >= 18);
}

这里的 age3 + 5age >= 18 都是表达式,因为它们都能产生结果。

5.2 复杂表达式

复杂表达式是多个表达式继续组合。

void main() {
  int age = 20;
  bool hasTicket = true;

  bool canEnter = age >= 18 && hasTicket;
  print(canEnter); // true
}

age >= 18 && hasTicket 就是一个复杂表达式。

它里面包含:

  • age >= 18:比较表达式。
  • hasTicket:布尔变量表达式。
  • &&:逻辑运算符。

最终整体产生一个 bool 结果。

5.3 条件表达式

条件表达式也叫三目运算符。

void main() {
  int score = 80;

  String result = score >= 60 ? '及格' : '不及格';
  print(result);
}

结构是:

条件 ? 条件成立时的结果 : 条件不成立时的结果

它适合处理比较短的二选一逻辑。

如果逻辑很长,建议使用 if else,可读性更好。

5.4 箭头函数表达式

箭头函数适合写只有一行返回结果的函数。

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

void main() {
  print(add(3, 5)); // 8
}

它等价于:

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

箭头函数不是为了炫技,而是让简单逻辑更简洁。

如果函数内部需要多行处理,就不要硬写成箭头函数。

5.5 表达式和语句的区别

表达式会产生结果,语句更像是一条完整指令。

void main() {
  int a = 3 + 5;
  print(a);
}

这里:

  • 3 + 5 是表达式,因为它产生结果 8
  • int a = 3 + 5; 是语句,因为它完成了一次变量声明和赋值。
  • print(a); 也是语句,因为它执行了一次打印动作。

不用把概念想复杂:表达式偏“算出一个东西”,语句偏“执行一件事情”。

5.6 运算符优先级

表达式里如果出现多个运算符,就会涉及优先级。

void main() {
  print(2 + 3 * 4);   // 14
  print((2 + 3) * 4); // 20
}

* 的优先级高于 +,所以第一行先算 3 * 4

如果你不确定优先级,或者希望别人一眼看懂,就加括号。

bool result = (age >= 18) && (hasTicket || isVip);

括号不是多余,它是在帮读者降低理解成本。

六、常见误区

6.1 把赋值和比较写混

初学者最常见的问题之一,就是分不清 ===

int age = 18;      // 赋值
bool ok = age == 18; // 比较

记住:

  • 一个等号是放值。
  • 两个等号是比较。

6.2 看到 ?? 就以为一定会修改变量

?? 不会修改变量,它只会返回一个结果。

void main() {
  String? name;

  String result = name ?? '游客';
  print(name);   // null
  print(result); // 游客
}

真正可能修改变量的是 ??=

void main() {
  String? name;

  name ??= '游客';
  print(name); // 游客
}

6.3 过度使用复杂表达式

表达式可以组合,但不代表越长越好。

bool canEnter = age >= 18 && hasTicket && !isBlocked && score > 60;

如果条件越来越多,可以拆成更清楚的变量。

bool isAdult = age >= 18;
bool canUseTicket = hasTicket && !isBlocked;
bool passedCheck = score > 60;

bool canEnter = isAdult && canUseTicket && passedCheck;

代码不是越短越好,而是越容易理解越好。

6.4 滥用非空断言

! 写起来很方便,但它不是消除空值问题,而是把风险推迟到运行时。

String? name;
print(name!.length); // 运行时风险

更稳的写法是先给默认值,或先判断。

String? name;
print(name?.length ?? 0);

6.5 忽略返回值类型

有些运算符看起来像数学里的符号,但返回值类型不一定和你想的一样。

void main() {
  print(5 / 2);  // 2.5,double
  print(5 ~/ 2); // 2,int
}

写 Dart 时要关注表达式结果是什么类型。尤其是赋值、函数返回值和条件判断里,类型不对会直接影响后续代码。

七、总结

这一篇我们系统梳理了 Dart 中的运算符与表达式。

运算符可以理解为代码中的工具按钮:算术运算符负责计算,赋值运算符负责更新变量,比较运算符负责判断关系,逻辑运算符负责组合条件,自增自减负责快速加减,空感知运算符负责处理 null,级联运算符负责连续操作同一个对象,类型判断运算符负责判断对象类型。

表达式则是由值、变量、运算符和函数调用组合出来的一段代码,它的特点是能产生结果。

学习这一节时,最重要的不是把所有符号一次背完,而是建立分类:

  • 这个符号解决什么问题?
  • 它返回什么结果?
  • 它会不会修改变量?
  • 它适合放在什么场景里?

把这几个问题想清楚,运算符和表达式就不再是一堆零散符号,而会变成你组织 Dart 代码的基础工具。

最后用一张表收束一下:

知识点 最该记住的点
/~/ / 得到 double~/ 得到整数结果
=== = 是赋值,== 是比较
&& 和 `
????= ?? 返回默认值,??= 才会修改变量
?.! ?. 是安全访问,! 是非空断言
.. 连续操作同一个对象
isis! 判断类型,并帮助 Dart 做类型提升
表达式 能产生结果的一段代码
posted @ 2026-07-12 17:55  好的佩奇  阅读(20)  评论(0)    收藏  举报