Skip to content

Dart支持下表所示的运算符。该表按从高到低的顺序显示了Dart的运算符结合性和优先级,这些是对Dart运算符关系的近似描述。您可以将其中许多运算符实现为类成员。

描述运算符结合性
后置一元运算符expr++、expr--、()、[]、?[]、.、?.、!
前置一元运算符-expr、!expr、~expr、++expr、--expr、await expr
乘法运算符*、/、%、~/
加法运算符+、-
移位运算符<<、>>、>>>
按位与&
按位异或^
按位或
关系和类型测试>=、>、<=、<、as、is、is!
相等性判断==、!=
逻辑与&&
逻辑或
空值判断??
条件运算符expr1 ? expr2 : expr3
级联运算符..、?..
赋值运算符=、*=、/=、+=、-=、&=、^=等
展开运算符(见注释)...、...?

警告
上表仅应作为参考指南。运算符优先级和结合性的概念是对语言语法中实际规则的近似描述。您可以在Dart语言规范定义的语法中找到Dart运算符关系的权威说明。

使用运算符时会创建表达式。以下是一些运算符表达式示例:

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

运算符优先级示例

在运算符表中,每个运算符的优先级都高于其下方行中的运算符。例如,乘法运算符%的优先级高于相等运算符==(因此先执行),而相等运算符==的优先级又高于逻辑与运算符&&。这种优先级意味着以下两行代码的执行方式相同:

dart
// 括号提高可读性
if ((n % i == 0) && (d % i == 0)) {
  // ...
}

// 可读性较差但等效
if (n % i == 0 && d % i == 0) {
  // ...
}

警告
对于需要两个操作数的运算符,最左侧的操作数决定使用哪个方法。例如,如果有Vector对象和Point对象,则aVector + aPoint使用Vector的加法(+)方法。

算术运算符

Dart支持常用的算术运算符,如下表所示:

运算符含义
+加法
-减法
-expr一元取反(也称为否定,反转表达式的符号)
*乘法
/除法(结果为双精度数)
~/除法(返回整数结果)
%取整数除法的余数(模运算)

示例

dart
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变量自增1(表达式值为var+1)
var++变量自增1(表达式值为var原值)
--var变量自减1(表达式值为var-1)
var--变量自减1(表达式值为var原值)

示例

dart
int a;
int b;

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

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

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

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

相等性和关系运算符

下表列出了相等性和关系运算符的含义:

运算符含义
==相等(见下文讨论)
!=不相等
>大于
<小于
>=大于或等于
<=小于或等于

要测试两个对象x和y是否表示同一事物,使用==运算符。(在极少数需要判断两个对象是否为完全相同实例的情况下,应使用identical()函数。)==运算符的工作原理如下:

  • 如果x或y为null,则当两者都为null时返回true,仅其中一个为null时返回false。
  • 返回x调用==方法并传入参数y的结果。(没错,像==这样的运算符是在第一个操作数上调用的方法。详情见运算符部分。)

示例

dart
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!如果对象不具有指定类型则为true

如果obj实现了T指定的接口,则obj is T的结果为true。例如,obj is Object?始终为true。

仅当确定对象属于特定类型时,才使用as运算符将其强制转换为该类型。示例

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

如果不确定对象是否为类型T,可先使用is T检查类型,再使用该对象:

dart
if (employee is Person) {
  // 类型检查通过
  employee.firstName = 'Bob';
}

注意
两段代码不等价。如果employee为null或不是Person类型,第一个示例会抛出异常,而第二个示例不执行任何操作。

赋值运算符

如前所述,可以使用=运算符赋值。若要仅在被赋值变量为null时赋值,可使用??=运算符:

dart
// 直接赋值给a
a = value;
// 若b为null则赋值给b,否则保持不变
b ??= value;

复合赋值运算符(如+=)将操作与赋值结合:

运算符等价于
a op= ba = a op b
示例:a += ba = a + b

示例

dart
var a = 2; // 使用=赋值
a *= 3; // 赋值并相乘:a = a * 3
assert(a == 6);

逻辑运算符

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

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

示例

dart
if (!done && (col == 0 || col == 3)) {
  // ...执行某些操作...
}

按位和移位运算符

在Dart中可以操作数字的各个二进制位,通常将这些按位和移位运算符与整数配合使用:

运算符含义
&按位与
^按位异或
~expr一元按位取反(0变1,1变0)
<<左移
>>右移
>>>无符号右移

注意
大整数或负操作数的按位运算行为可能因平台而异。更多信息请查看按位运算的平台差异

示例

dart
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); // 右移

// 右移示例(在Web平台上因操作数被掩码为32位而行为不同)
assert((-value >> 4) == -0x03);

assert((value >>> 4) == 0x02); // 无符号右移
assert((-value >>> 4) > 0); // 无符号右移结果为正数

版本说明

运算符(又称无符号右移或三目移位)要求语言版本至少为2.14。

条件表达式

Dart有两个运算符可简洁地计算原本需要if-else语句的表达式:

  1. 条件运算符condition ? expr1 : expr2
    • 如果condition为true,计算expr1(并返回其值);否则计算并返回expr2的值。
  2. 空值合并运算符expr1 ?? expr2
    • 如果expr1不为null,返回其值;否则计算并返回expr2的值。

当需要根据布尔表达式赋值时,可使用条件运算符?和::

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

如果布尔表达式检查null,可使用空值合并运算符??:

dart
String playerName(String? name) => name ?? 'Guest';

上述示例还可以用至少两种其他方式编写,但不够简洁:

dart
// 使用?:运算符的较长版本
String playerName(String? name) => name != null ? name : 'Guest';

// 使用if-else语句的冗长版本
String playerName(String? name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

级联表示法

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

示例

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

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

上述示例等效于:

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

如果级联操作的对象可能为null,则对第一个操作使用空值安全级联(?..)。以?..开头可确保不会对null对象执行任何级联操作:

dart
document.querySelector('#confirm') // 获取对象
  ?..textContent = 'Confirm' // 使用其成员
  ..classList.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

等效于:

dart
final button = document.querySelector('#confirm');
button?.textContent = 'Confirm';
button?.classList.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();

还可以嵌套级联:

dart
final addressBook =
    (AddressBookBuilder()
          ..name = 'jenny'
          ..email = 'jenny@example.com'
          ..phone =
              (PhoneNumberBuilder()
                    ..number = '415-555-0100'
                    ..label = 'home')
                  .build())
        .build();

需注意:级联应作用于返回实际对象的函数。例如,以下代码会报错:

dart
var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // 错误:void类型没有'write'方法

sb.write()调用返回void,无法对void执行级联操作。

注意
严格来说,级联的“双点”表示法不是运算符,它只是Dart语法的一部分。

dart
[...a + b] // 合法,a + b 需返回集合

其他运算符

以下是其他常见运算符的说明:

运算符名称含义
()函数调用执行函数调用,如 foo()list.length(方法调用本质也是函数调用)
[]下标访问调用可重写的 [] 运算符,如 list[0] 等价于 list.[]!(0)
?[]条件下标访问左操作数可为 null,如 list?[0] 等价于 list == null ? null : list[0]
.成员访问访问对象属性或方法,如 obj.nameobj.method()
?.条件成员访问左操作数可为 null,如 obj?.name 等价于 obj == null ? null : obj.name
!非空断言将表达式强制转换为非空类型,若为 null 抛出运行时异常,如 obj!.name

运算符重载

Dart 允许通过实现类成员方法来重载部分运算符,常见可重载的运算符及其对应方法如下:

运算符对应方法示例(在类中定义)
+operator +(other)Vector operator +(Vector other) => Vector(x + other.x, y + other.y);
-operator -()Vector operator -() => Vector(-x, -y);(单目取反)
[]operator [](index)int operator [](int index) => _items[index];
[]=operator []=(index, value)void operator []=(int index, int value) => _items[index] = value;
==operator ==(other)bool operator ==(Object other) => other is Vector && x == other.x && y == other.y;
>operator >(other)bool operator >(Vector other) => x * x + y * y > other.x * other.x + other.y * other.y;

注意

  • 运算符重载仅适用于自定义类,内置类型(如 intString)的运算符行为不可改变。
  • == 运算符重载时,建议同时重写 hashCode 方法以保证哈希一致性。

总结

Dart 的运算符系统设计灵活,既保留了传统编程语言的习惯,又通过级联、展开、空安全等特性增强了表达能力。在实际开发中,需注意:

  1. 利用空安全运算符(?.??!)避免 null 相关错误。
  2. 通过括号明确优先级,提高代码可读性(如 (a + b) * c 优于 a + b * c)。
  3. 合理使用级联符号简化对象配置代码,但避免在 void 返回值上使用。
  4. 自定义类时,通过运算符重载提升接口友好性(如向量运算、数据结构操作)。

更多细节请参考 Dart 语言规范中的运算符章节Effective Dart 中的运算符使用指南