Dart 运算符

506 阅读7分钟

Dart定义了下表中显示的操作符。您可以重写其中的许多操作符,如可重写操作符中所述

描述运算符
一元后缀表达式++ 表达式-- () [] . ?.
一元前缀-表达式 !表达式 ~表达式 ++表达式 --表达式
乘除法* / % ~/
加减法+ -
位运算<< >> >>>
二进制与&
二进制异或
二进制或
关系和类型测试> < >= <= as is is!
相等判断== !=
逻辑与&&
逻辑或
空判断??
条件表达式表达式1 ? 表达式2 : 表达式3
expr1 ?? expr2
级联..
赋值= += -= *= /= %= ~/= >>= <<= &= ^= ??= 等等……
请注意:上述运算符优先级是对 Dart 解析器行为的效仿。更准确的描述,请参阅 Dart 语言规范 中的语法

一旦你使用了运算符,就创建了表达式。下面是一些运算符表达式的示例:

a++
a + b
a = b
a == b
c ? a : b
a is T

运算符表 中,运算符的优先级按先后排列

  • 即第一行优先级最高,最后一行优先级最低
  • 而同一行中,最左边的优先级最高,最右边的优先级最低
  • 括号()可以提高可读性

例如:% 运算符优先级高于 == ,而 == 高于 &&。根据优先级规则,那么意味着以下两行代码执行的效果相同:

// 括号提高了可读性。
// Parentheses improve readability.
if ((n % i == 0) && (d % i == 0)) ...
// 难以理解,但是与上面的代码效果一样。
if (n % i == 0 && d % i == 0) ...

⚠️**注意: **对于有两个操作数的运算符,左边的操作数决定了运算符的功能。比如如果有一个 Vector 对象和一个 Point 对象,表达式 aVector + aPoint 中所使用的是 Vector 对象中定义的 + 运算符

算术运算符

Dart 支持常用的算术运算符

运算符描述例子
+
-
*
/
%取模
-表达式一元负, 也可以作为反转(反转表达式的符号)
~/除并取整
++var var++递加,分为前加和后加var = var + 1 (表达式的值为 var + 1)
var = var + 1 (表达式的值为 var)
--var var--递减,分为前递减和后递减var = var – 1 (表达式的值为 var – 1)
var = var – 1 (表达式的值为 var)
main(List<String> args) {
  assert(2 + 3 == 5);
  assert(2 - 3 == -1);
  assert(2 * 3 == 6);
  assert(5 / 2 == 2.5); // 结果是一个浮点数
  assert(5 ~/ 2 == 2);  // 结果是一个整数
  assert(5 % 2 == 1);   // 取余
  assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

  var i = 0, j = 0; j = ++i; // 在 j 赋值前将 i 增加 1
  print('i=${i} j=${j}');  //i=1 j=1
  assert(i == j);         

  i = 0; j = i++; // 在 j 赋值后将 i 增加 1
  print('i=${i} j=${j}');  //i=1 j=0
  assert(j == i); // 0 != 1

  var x = 0, y = 0;
  print('++x=${++x} 加完后再输出x=${x}'); //++x=1 加完后再输出x=1
  print('y++=${y++} 加完后再输出y=${y}'); //y++=0 加完后再输出y=1

  var a = 0, b = 0;
  print('--a=${--a} 加完后再输出a=${a}'); //--a=-1 加完后再输出a=-1
  print('b--=${b--} 加完后再输出b=${b}'); //b--=0 加完后再输出b=-1
}

赋值运算符

可以使用 = 来赋值,同时也可以使用 ??= 来为值为 null 的变量赋值

a = value; // 将 value 赋值给 a (Assign value to a)
b ??= value; // 当且仅当 b 为 null 时才赋值

像这样的赋值运算符将算数运算符和赋值运算符组合在了一起

=+=-=*=/=%=~/=
??=&==^=>>=<<=

下表解释了符合运算符的原理:

场景复合运算等效表达式
假设有运算符 opa op= ba = a op b
示例:a += ba = a + b

下面的例子展示了如何使用赋值以及复合赋值运算符:

var a = 2; // 使用 = 赋值 (Assign using =)
a *= 3; // 赋值并做乘法运算 Assign and multiply: a = a * 3
assert(a == 6);

关系运算符

下表列出了关系运算符及含义

运算符描述
==相等
!=不等
>大于
<小于
>=大于等于
<=小于等于

要判断两个对象 x 和 y 是否表示相同的事物使用 == 即可。(在极少数情况下,可能需要使用 identical() 函数来确定两个对象是否完全相同。)。下面是 == 运算符的一些规则:

  1. 假设有变量 x 和 y,且 x 和 y 至少有一个为 null,则当且仅当 x 和 y 均为 null 时 x == y 才会返回 true,否则只有一个为 null 则返回 false
  2. x.==(y) 将会返回值,这里不管有没有 y,即 y 是可选的。也就是说 == 其实是 x 中的一个方法,并且可以被重写。详情请查阅重写运算符

下面的代码给出了每一种关系运算符的示例:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

逻辑运算符

使用逻辑运算符你可以反转或组合布尔表达式

运算符描述
&&逻辑与
逻辑或
!表达式取非
对表达式结果取反(即将 true 变为 false,false 变为 true)

下面是使用逻辑表达式的示例:

if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

按位和移位运算符

在 Dart 中,二进制位运算符可以操作二进制的某一位,但仅适用于整数

运算符描述
&按位与
按位或
按位异或
<<位左移
>>位右移
~表达式按位取反(即将 “0” 变为 “1”,“1” 变为 “0”)

下面是使用按位和移位运算符的示例:

final value = 0x22;
final bitmask = 0x0f;
assert((value & bitmask) == 0x02);  // 按位与 (AND)
assert((value & ~bitmask) == 0x20); // 取反后按位与 (AND NOT)
assert((value | bitmask) == 0x2f);  // 按位或 (OR)
assert((value ^ bitmask) == 0x2d);  // 按位异或 (XOR)
assert((value << 4) == 0x220);      // 位左移 (Shift left)
assert((value >> 4) == 0x02);       // 位右移 (Shift right)

条件运算符

条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量

运算符描述
? :条件表达式condition ? expr1 : expr2
??空判断expr1 ?? expr2

条件表达式 Dart 有两个特殊的运算符可以用来替代 if-else 语句, 这两个运算符可以看作是特殊的关系比较符:

如果条件为真,则计算expr1(并返回其值);否则,计算并返回expr2的值
condition ? expr1 : expr2
var visibility = isPublic ? 'public' : 'private';

如果expr1是非空null的,则返回其值; 否则,计算并返回expr2的值
expr1 ?? expr2
String playerName(String name) => name ?? 'Guest';

上述示例还可以写成至少下面两种不同的形式,只是不够简洁:

// 相对使用 ?: 运算符来说稍微长了点。(Slightly longer version uses ?: operator).
String playerName(String name) => name != null ? name : 'Guest';

// 如果使用 if-else 则更长
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

类型判断运算符

as、is、is! 运算符是在运行时判断对象类型的运算符

运算符描述
as类型转换(也用作指定类前缀))
is如果对象是指定类型则返回 true
is!如果对象是指定类型则返回 false

当且仅当 obj 实现了 T 的接口,obj is T 才是 true, 例如 obj is Object 总为 true,因为所有类都是 Object 的子类

仅当你确定这个对象是该类型的时候,你才可以使用 as 操作符可以把对象转换为特定的类型。例如:

(emp as Person).firstName = 'Bob';

如果你不确定这个对象类型是不是 T,请在转型前使用 is T 检查类型。

if (emp is Person) {
  // 类型检查
  emp.firstName = 'Bob';
}

你可以使用 as 运算符进行缩写:

(emp as Person).firstName = 'Bob';

⚠️备忘: 上述两种方式是有区别的:如果 emp 为 null 或者不为 Person 类型,则第一种方式将会抛出异常,而第二种不会

级联运算符(..)

级联运算符(..)可以让你在同一个对象上连续调用多个对象的变量或方法 比如下面的代码:

querySelector('#confirm') // 获取对象 (Get an object).
  ..text = 'Confirm' // 使用对象的成员 (Use its members).
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));
  • 第一个方法 querySelector 返回了一个 Selector 对象
  • 后面的级联操作符都是调用这个 Selector 对象的成员并忽略每个操作的返回值

上面的代码相当于:

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 (Error: method 'write' isn't defined for 'void').

上述代码中的 sb.write() 方法返回的是 void,返回值为 void 的方法则不能使用级联运算符 ⚠️备忘: 严格来说 .. 级联操作并非一个运算符而是 Dart 的特殊语法

其他运算符

大多数其它的运算符,已经在其它的示例中使用过:

运算符名字描述
()使用方法代表调用一个方法
[]访问 List访问 List 中特定位置的元素
.访问成员成员访问符
?.条件访问成员与上述成员访问符类似,但是左边的操作对象不能为 null,例如 foo?.bar,如果 foo 为 null 则返回 null ,否则返回 bar

更多关于 . ?. 和 .. 运算符介绍,请参考