Dart 定义了下面表格中的这些运算符。你可以重写其中大部分的运算符,在 重载运算符 部分有更详细的描述。
描述 | 运算符 |
---|---|
一元后缀 | expr++ expr-- () [] . ?. |
一元前缀 | -expr !expr ~expr ++expr --expr |
乘除 | * / % ~/ |
加减 | + - |
移位 | << >> |
按位与 | & |
按位异或 | ^ |
按位或 | | |
关系和类型检查 | >= > <= < as is is! |
相等性 | == != |
逻辑与 | && |
逻辑或 | || |
如果为空 | ?? |
条件 | expr1 ? expr2 : expr3 |
级联 | .. |
赋值 | = *= /= ~/= %= += -= <<= >>= &= ^= |= ??= |
使用运算符创建的是表达式。下面是一些表达式的例子:
a++ a + b a = b a == b c ? a : b a is T
在 运算符表 中,每一个表达式都比它下面的表达式优先级高。比如,取余运算符 % 的优先级就比等于运算符高(因此先执行),而等于运算符的优先级又比逻辑与运算符 && 高。优先级意味着下面两行代码的执行方式相同:
// 括号可提高可读性 if ((n % i == 0) && (d % i == 0)) ... // 可读性差,但是效果等价 if (n % i == 0 && d % i == 0) ...
警告:对于作用于两个操作数的操作符,最左边的操作数决定了哪个版本的运算符会被使用。比如,如果你有一个 Vector 对象和 Point 对象,aVector + aPoint 使用 Vector 版本的 +。
算术运算符
Dart 支持通常的算术运算符,如下表所示。
运算符 | 含义 |
---|---|
+ | 加 |
- | 减 |
-expr | 取反(改变一个表达式的正负号) |
* | 乘 |
/ | 除 |
~/ | 整数除,返回整数结果 |
% | 获取整数除的余数(取模) |
例子:
assert(2 + 3 == 5); assert(2 - 3 == -1); assert(2 * 3 == 6); assert(5 / 2 == 2.5); // 结果是 double assert(5 ~/ 2 == 2); // 结果是 int assert(5 % 2 == 1); // 取余 assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart 也支持前缀和后缀的自增、自减运算符:
运算符 | 含义 |
---|---|
++var | var = var + 1(表达式的值是 var + 1) |
var-- | var = var + 1(表达式的值是 var) |
--var | var = var -1(表达式的值是 var - 1) |
var-- | var = var - 1(表达式的值是 var) |
例子:
var a, b; a = 0; b = ++a; // 在过取值之前自增 assert(a == b); // 1 == 1 a = 0; b = a++; // 在取值之后自增 assert(a != b); // 1 != 0 a = 0; b = --a; // 在取值之前自减 assert(a == b); // -1 == -1 a = 0; b = a--; // 在取值之后自减 assert(a != b); // -1 != 0
相等和关系运算符
下表列出了相等和关系运算符的含义。
运算符 | 含义 |
---|---|
== | 相等;请参阅下面的讨论 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
要判断两个对象 x 和 y 是否代表相同的东西,使用 == 运算符。(在少数情况下,当你想知道两个对象是否是同一个对象,使用 identical()。)下面是 == 运算符的工作原理:
- 如果 x 或者 y 是空,仅当两者都是空时返回 true,否则返回 false。
- 返回方法 x.==(y) 的调用结果。(是的,像 == 这样的运算符就是在它第一个操作数上调用的方法。你甚至可以重载许多运算符,包括 ==
这里是一些使用相等和关系运算符的例子:
assert(2 == 2); assert(2 != 3); assert(3 > 2); assert(2 < 3); assert(3 >= 3); assert(2 <= 3);
类型检查运算符
运算符 as、is 和 is! 可以在运行期方便地进行类型检查。
运算符 | 含义 |
---|---|
as | 类型转换 |
is | 如果对象拥有指定类型返回 true |
is! | 如果对象拥有指定类型返回 false |
如果 obj 实现了接口 T ,那么表达式 obj is T 的结果是 true。比如,obj is Object 总是返回 true。
使用 as 运算符将对象转换成指定类型。通常的,你应该使用它作为跟随在 is 后面使用该对象作为表达式这种情况的简写。比如,考虑以下代码:
if (emp is Person) { // 类型检查 emp.firstName = 'Bob'; }
你可以使用 as 运算符使代码更简洁:
(emp as Person).firstName = 'Bob';
说明:上面的代码并不是完全等价的。如果 emp 是空或者不是 Person,第一个例子(使用 is)什么都不做,而第二个(使用 as)会抛出一个异常。
赋值运算符
如你所见,你可以使用 = 运算符进行赋值。要在仅当被赋值的变量为空时才进行赋值,使用 ??= 运算符。
// 赋值到 a a = value; // 如果 b 为空才赋值到 b;否则 b 保留原始值 b ??= value;
复合赋值运算符比如 += 组合了一个其他运算符到赋值运算符。
= | -= | /= | %= | >>= | ^= |
---|---|---|---|---|---|
+= | *= | ~/ | <<= | &= | |= |
下面解释复合赋值运算符的原理:
复合赋值 | 等价的表达式 | |
---|---|---|
对运算符 op: | a op= b | a = a op b |
例子: | a += b | a = a + b |
下面的例子同时使用了赋值运算符和复合赋值运算符:
var a = 2; // 使用 = 赋值 a *= 3; // 赋值并进行乘法:a = a * 3 assert(a == 6);
逻辑运算符
你可以使用逻辑运算符反转或者组合布尔表达式。
运算符 | 含义 |
---|---|
!expr | 反转后面表达式的值(false 变 true,true 变 false) |
|| | 逻辑或 |
&& | 逻辑与 |
下面是一个使用逻辑运算符的例子:
if (!done && (col == 0 || col == 3)) { // ...一些操作... }
按位和移位运算符
在 Dart 中你可以操作数值中的各个位。通常你需要在整数上使用这些按位和移位运算符。
运算符 | 含义 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~expr | 一元按位补码(0s 变成 1s;1s 变成 0s) |
<< | 左移位 |
>> | 右移位 |
下面是使用按位和移位操作符的例子:
final value = 0x22; final bitmask = 0x0f; assert((value & bitmask) == 0x02); // 按位与 assert((value & ~bitmask) == 0x20); // 按位与、按位补码 assert((value | bitmask) == 0x2f); // 按位或 assert((value ^ bitmask) == 0x2d); // 按位异或 assert((value << 4) == 0x220); // 左移位 assert((value >> 4) == 0x02); // 右移位
条件表达式
Dart 有两个表达式可以让你简明地计算可能需要 if-else 语句的表达式:
condition ? expr1 : expr2
如果 condition 是 true,计算 expr1(并且返回它的值);否则,计算并返回 expr2 的值。
expr1 ?? expr 2
如果 expr1 非空,返回它的值;否则,计算并返回 expr2 的值。
当你需要根据一个布尔表达式进行赋值时,考虑使用 **?:**。
var visibility = isPublic ? 'public' : 'private';
如果布尔表达式进行的是空值判断,考虑使用 **??**。
String playerName(String name) => name ?? 'Guest';
上一个例子至少可以改写为以下两种形式,但是没有那么简洁:
// 比使用 ?: 运算符的长一点 String playerName(String name) => name != null ? name : 'Guest'; // 使用 if-else 语句的长版本 String playerName(String name) { if (name != null) { return name; } else { return 'Guest'; } }
级联符号
级联 (..) 允许你在同一个对象上进行一系列操作。除了方法调用,你也可以访问同一个对象的属性。这样通常可以让你免于创建临时变量并且写出更加顺畅的代码。
考虑以下代码:
querySelector('#confirm') // 获取一个对象 ..text = 'Confirm' // 使用它的成员 ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
第一个方法调用,**querySelector()**,返回一个选择器对象。作用在这个选择器对象上的级联标记后面的代码,会忽略所有随后可能会有的返回值。
前面的代码等同于:
var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!'));
你也可以嵌套级联。比如:
final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
请小心地在那些返回真实对象的方法上构造级联。比如,下面的调用会失败:
var sb = StringBuffer(); sb.write('foo') ..write('bar'); // 错误:void 没有定义 'write' 方法
代码 sb.write() 返回 void,你不能在 void 上构造级联。
说明:严格地说,级联的”两点“标记不是一个运算符。它是 Dart 语法的一部分。
其他运算符
你已经在其他例子中看到过剩下的大部分运算符了:
运算符 | 名称 | 含义 |
---|---|---|
() | 函数调用 | 代表一个函数调用 |
[] | 列表访问 | 引用列表中指定索引处的值 |
. | 成员访问 | 引用一个表达式的属性;比如:foo.bar 选取了表达式 foo 的 bar 属性 |
?. | 有条件的成员访问 | 类似 .**,但是左操作数可以为空;比如:foo.bar** 选取了表达式 foo 的 bar 属性除非 foo 是空(在这种情况下 foo?.bar 的值是空) |