Dart之运算符与表达式

发布时间:2026/7/13 3:31:49
Dart之运算符与表达式 一、前言刚开始学 Dart 时很多人会先看到一堆符号、-、*、/、、、、??、..。这些符号看起来很零散好像只能靠背。其实不用这样理解。你可以先把运算符想象成代码里的“工具按钮”有的负责计算有的负责赋值有的负责比较有的负责判断条件还有的专门处理空值。表达式则是这些工具组合出来的一句话。比如3 5是一个表达式age 18也是一个表达式name ?? 游客也是一个表达式。这一篇我们就用系统化的方式把 Dart 中常见的运算符和表达式讲清楚。二、一句话理解运算符是 Dart 用来完成计算、赋值、比较、判断等操作的符号表达式是由变量、值、运算符和函数调用组合而成并且能产生结果的一段代码。简单说运算符告诉代码“要做什么操作”。表达式把操作组合起来得到一个结果。学习重点不要死背符号要理解每类符号解决什么问题。三、什么是运算符运算符就是代码中的操作符号。在生活中我们会用不同工具处理不同事情剪刀用来剪胶水用来粘计算器用来算数。代码里的运算符也是这样不同符号负责不同操作。先建立一张总表类型常见符号作用常见结果算术运算符、-、*、/、~/、%做数学计算数值赋值运算符、、-、*、/、~/、%给变量赋值或更新变量更新变量比较运算符、、、、、!比较两个值bool逻辑运算符、、!自增自减、--快速加 1 或减 1更新变量空感知运算符??、??、?.处理null取决于表达式级联运算符..、?..连续操作同一个对象原对象类型判断is、is!判断对象类型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 ba a b在原值上增加a - ba a - b在原值上减少a * ba a * b在原值上相乘a / ba a / b在原值上除以a ~/ ba a ~/ b在原值上整除a % ba 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。!取反把true变false把false变true。可以用生活场景理解进电影院成年人并且有票才能进场。找交通方式坐地铁或者打车有一个可行就能出发。取反不是会员就走非会员逻辑。逻辑运算符还有一个很重要的特点短路。遇到左边为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 }如果name是null就不会继续调用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(); }这里user是null所以后面的设置和方法调用不会继续执行。使用级联时要注意级联操作的目标始终是最开始的那个对象而不是每一步调用的返回值。4.4 类型判断运算符类型判断运算符用于判断一个对象是不是某种类型。void main() { Object value hello; print(value is String); // true print(value is! int); // true }is判断是不是某个类型。is!判断不是不是某个类型。这在处理Object、dynamic或复杂数据时很常见。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); }这里的age、3 5、age 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.5double print(5 ~/ 2); // 2int }写 Dart 时要关注表达式结果是什么类型。尤其是赋值、函数返回值和条件判断里类型不对会直接影响后续代码。七、总结这一篇我们系统梳理了 Dart 中的运算符与表达式。运算符可以理解为代码中的工具按钮算术运算符负责计算赋值运算符负责更新变量比较运算符负责判断关系逻辑运算符负责组合条件自增自减负责快速加减空感知运算符负责处理null级联运算符负责连续操作同一个对象类型判断运算符负责判断对象类型。表达式则是由值、变量、运算符和函数调用组合出来的一段代码它的特点是能产生结果。学习这一节时最重要的不是把所有符号一次背完而是建立分类这个符号解决什么问题它返回什么结果它会不会修改变量它适合放在什么场景里把这几个问题想清楚运算符和表达式就不再是一堆零散符号而会变成你组织 Dart 代码的基础工具。最后用一张表收束一下知识点最该记住的点/和~//得到double~/得到整数结果和是赋值是比较和 ??和????返回默认值??才会修改变量?.和!?.是安全访问!是非空断言..连续操作同一个对象is和is!判断类型并帮助 Dart 做类型提升表达式能产生结果的一段代码