Dart学习笔记2-运算符

99 阅读9分钟

Operators 运算符

概念

dart支持下面所列的运算符。以下列表大概的从高到低的显示了Dart的运算符的结合性和运算符优先级。您可以将其中许多运算符实现为类成员。

(Associativity:结合性。结合性指的是运算符在一条语句中出现多次时的优先级顺序。 对于同一优先级的运算符而言,它们的结合性决定了它们之间的计算顺序。 Dart中共有三种结合性:从左到右(左结合)、从右到左(右结合)和无结合性。)

运算符表

描述运算符结合性
一元后缀expr++ expr-- () [] ?[] . ?. !
一元前缀-expr !expr ~expr ++expr --expr await expr
乘法类* / % ~/
加法类+ -
位运算<< >> >>>
按位与&
按位异或
按位或|
关系测试和类型测试>= > <= < as is is!
等于== !=
逻辑与&&
逻辑或||
如果为 null??
条件语句expr1 ? expr2 : expr3
cascade(联级).. ?..
赋值= *= /= += -= &= ^= etc.

警告: 上表仅供参考。运算符优先级和关联性的概念是一种语言语法中的核心真理。你可以在Dart语言规范定义的语法中找到关于dart运算符关系的权威表现。

当你使用运算符时,你就创建了表达式。这里是一些运算符表达式的例子:

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(断言),作用是 如果表达式的求值结果不满足需要,则打断代码的执行。
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还支持前缀和后缀的增量和减量运算符。

运算符含义
++varvar = var + 1 (++val作为表达式时,值是 var + 1)
var++var = var + 1 (val++ 作为表达式时,值是 var)
--varvar = var - 1 (++val作为表达式时,值是 var - 1)
var--var = var - 1 (val-- 作为表达式时,值是 var)

例子:

int a;
int b;

a = 0;
b = ++a; // 在赋值给b前,a先自增1
assert(a == b); // 1 == 1

a = 0;
b = a++; // 赋值给b后,a再自增1
assert(a != b); // 1 != 0

a = 0;
b = --a; // 在赋值给b前,a先自减1
assert(a == b); // -1 == -1

a = 0;
b = a--; // 赋值给b后,a再自减1
assert(a != b); // -1 != 0

相等运算符和关系运算符

下面的表格列出了相等运算符和关系运算符的含义。

运算符含义
==等于;请参阅下面的讨论
>大于
<小于
>=大于等于
<=小于等于

要测试两个对象x和y是否表示相同的东西,请使用==运算符。(在极少数情况下,您需要知道两个对象是否是完全相同的对象,请改用idential()函数。)以下是==运算符的工作方式:

  1. 如果 x 或者 y 为 null,当他们都为null时返回true,当只有一个为null时返回false
  2. 返回在 x 上用参数 y 调用 == 方法的结果。(没错,像 == 这样的运算符是在其第一个操作数上调用的方法。有关详细信息,请参阅运算符。)

以下是使用每个相等运算符和关系运算符的示例:

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

类型检测运算符

as,is,和 is!运算符非常方便在运行时进行类型检查

运算符含义
asTypecast(也用于指定库前缀)
is如果对象具有指定的类型,则为True
is!如果对象没有指定的类型,则为True

如果obj实现了T指定的接口,则obj is T的结果为true。举个例子: obj is Object 结果总是为true

当且仅当你确认某个对象为某个特定类型时,你可以使用as运算符将该对象强制转化为该类型,例如:

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

如果你不确定该对象是否为T类型,就可以在你使用该对象之前,用is T 来检查对应的类型,例如:

if(employee is Person) {
  // Type check
  employee.firstName = 'Bob';
}

小提示:以上两个例子的代码不等效,如果employee为null或不是Person,则第一个示例抛出异常;第二个什么也不做。

赋值运算符

就像你已经看到的,你可以使用=运算符来赋值。如果仅当被赋值的变量为null时才给它赋值,请使用 ??=运算符。

// 给a赋值
a = value;
// 如果b为空,则为b赋值;否则,b保持不变
b ??= value;

复合赋值运算符,如+=将运算与赋值组合在一起。

| | | |

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

以下是符合赋值运算符如何运行的:

| 复合赋值 | 等效表达式

---------| ----- | --------- 操作符操作:| a op= b | a = a op b 例如:| a += b | a = a + b

下面的这个例子使用了赋值运算符和复合赋值运算符:

var a = 2; // Assign using =
a *= 3; // Assign and multiply: a = a * 3
assert(a == 6);

逻辑运算符

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

运算符含义
!expr反转表达式(将false更改为true,反之亦然)
| |逻辑或
&&逻辑与

这是一个使用逻辑运算符的例子:

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

按位和移位运算符

您可以在Dart中操作数字的各个位。通常,您会将这些按位和移位运算符与整数一起使用。

运算符含义
&
|
异或
~expr一元逐位补码(0变为1;1变为0)
<<左移位
>>右移位
>>>无符号右移位

提示:具有大操作数或负操作数的按位运算的行为可能因平台而异。要了解更多信息,请查看Bitwise操作平台的差异

这是一个使用按位和移位运算符的例子:

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

// Shift right example that results in different behavior on web
// because the operand value changes when masked to 32 bits:
assert((-value >> 4) == -0x03);

assert((value >>> 4) == 0x02); // Unsigned shift right
assert((-value >>> 4) > 0); // Unsigned shift right

版本提示:>>>操作符要求的语言版本至少为2.14

条件表达式

Dart有两个运算符,可以让您简明地计算可能需要if-else语句的表达式:

condition ? expr1 : expr2

如果条件为真,计算expr1(并返回其值);否则,计算并返回expr2的值。

expr1 ?? expr2

如果expr1 是 非null的值,则返回expr1的值。否则,就计算并返回 expr2 的值。

当您需要基于布尔表达式分配值时,请考虑使用 condition ? expr1 : expr2

var visibility = isPublic ? 'public' : 'private';

如果布尔表达式测试为null,考虑使用expr1 ?? expr2

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';
  }
}

级联表示法

级联(..,?..)允许您对同一对象进行一系列操作。除了访问实例成员之外,还可以调用同一对象上的实例方法。这通常可以省去创建临时变量的步骤,并允许您编写更流畅的代码。

请思考以下代码:

var paint = Paint()
  ..color = Colors.block
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0

构造函数Paint()返回一个Paint对象。遵循级联表示法的代码对该对象进行操作,忽略可能返回的任何值。

前面的示例等效于以下的代码:

var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

如果级联操作的对象可以为null,则使用空短路级联(?..)进行第一次操作。从?..保证不会在该null对象上尝试任何级联操作。

querySelector('#confirm') // Get an object.
  ?..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

版本提示: ?.. 语法要求语言版本至少为2.12

前面的代码等效于以下代码:

var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();

你也可以嵌套级联。例如

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

备注: 严格地说,级联的“双点”表示法不是运算符。这只是Dart语法的一部分。

其它运算符

您已经在其他示例中看到了大多数剩余的运算符:

运算符名称含义
()函数应用表示函数调用
[]下标访问表示对可重写[]运算符的调用;示例:fooList[1]将int 1传递给fooList以访问索引1处的元素
?[]条件下标访问与[]类似,但最左边的操作数可以为null;例如:fooList?[1] 将int 1传递给fooList以访问索引1处的元素,除非fooList为null(在这种情况下,表达式的计算结果为null)
.成员访问指表达式的属性;示例:foo.bar从表达式foo中选择属性栏
?.条件成员访问类似. 但是左边的操作数可以为null;例如:foo?.bar 如果foo不为null的话,从表达式foo中访问bar属性。(如果foo为null,则foo?.bar也为null)
Null断言运算符将表达式转换为其基础的不可为null的类型,如果转换失败,则引发运行时异常;例如:foo!.bar 断言 foo是一个非空表达式,且访问该表达式的bar属性。如果foo为null,则会抛出一个运行时错误