Dart之旅04: 运算符
有些语言翻译为运算符,有些叫操作符,都指的是Operator。
Dart定义的操作符都在下表中。你可以像C++或者Kotlin一样重写大部分操作符,这些操作符又叫可重载操作符。在类的章节详述。
描述 | 操作符 |
---|---|
一元后缀 | expr++ expr-- () [] . ?. |
一元前缀 | -expr !expr ~expr ++expr --expr |
乘法级别 | * / % ~/ |
加法级别 | + - |
进位 | << >> |
按位与 | & |
按位异或 | ^ |
按位或 | | |
大小关系和类型判断 | >= > <= < as is is! |
相等 | == != |
逻辑与 | && |
逻辑或 | || |
if null | ?? |
三元运算 | expr1 ? expr2 : expr3 |
及联 | .. |
赋值 | = *= /= ~/= %= += -= <<= >>= &= ^= |= ??= |
当你使用操作符时,你就是在创建表达式:
a++
a + b
a = b
a == b
c ? a : b
a is T
上面的操作符列表中,每一行都比下一行的优先级要高。
如果两个对象都重载了相同二元操作符,那么本次重载使用操作符使用左侧操作数的重载方法。比如Vector对象+Point对象
aVector+aPoint
的结果是调用Vector对象的加号重载方法。
算术运算符
dart支持的算术运算符如下表:
Operator | Meaning |
---|---|
+ | Add |
- | Subtract |
-expr | Unary minus, also known as negation (reverse the sign of the expression) |
* | Multiply |
/ | Divide |
~/ | Divide, returning an integer result |
% | Get the remainder of an integer division(modulo) |
Example:
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart同样支持前缀和后缀自增自减运算符:
Operator | Meaning |
---|---|
++var | var = var + 1 (表达式值为var+1) |
var++ | var = var + 1 (表达式值为var) |
--var | var = var – 1 (表达式值为var – 1) |
var-- | var = var – 1 (表达式值为var) |
Example:
var a, b;
a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1
a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0
a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1
a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0
这里和大部分语言都是一样的。
等式和关系运算符
下表是等式和关系运算符的含义:
Operator | Meaning |
---|---|
== | 相等;见下面讨论 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
通常而言,dart的==
操作符都是用来判断两个变量的内容是否相等。这和大部分语言都一样,但如果你想精确的确定两个变量指向的对象是一个对象,那么你可以使用identical()
方法来代替。这里说明下==
的工作原理:
- 如果x或者y是null,那么如果两者皆空则返回true
- 两者都不为空,调用
x.==(y)
。这里==
先暂时看作一个函数名,更详细的参考操作符重载部分。
下面是一个例子:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
类型测试操作符
as
, is
and is!
都是运行时的类型测试操作符
Operator | Meaning |
---|---|
as | 类型转换(在import时也可以用来指定库前缀) |
is | 如果对象是指定类型返回true(相当于java的instanceof) |
is! | is的取反 |
如上所说,as就是java中的类型强转,用来将父类转换成子类。在dart中同样有智能转换的概念(和kotlin一样):
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
当使用is
判断之后emp
其实在if
语句中隐式的被认为了是Person对象。这在Kotlin中叫做智能转换,dart文档中并没有相关的命名。
你可以通过as
来让代码变得更短:
(emp as Person).firstName = 'Bob';
注意:这里推荐使用
is
判断,因为as
的本质是类型强转,强转失败会产生类型转换异常。而is
则会什么也不做。
赋值操作符
赋值运算符也就是=
的用法和其他语言基本一致。如果一个变量只有在null的情况下接受赋值可以使用??=
:
// Assign value to a
a = value;
// 如果b是null,则赋值为value,否则b保持不变
b ??= value;
复合赋值运算符(例如+=
,<<=
)就是原有运算符和赋值运算符的结合。这里不再赘述了,有关复合赋值运算符的重载,参考运算符重载部分。
逻辑运算符
逻辑运算符就是!
, ||
, &&
这个和java,php或者c语言都是一样的。
位运算符
如下表,和常用语言相通:
Operator | Meaning |
---|---|
& | AND |
| | OR |
^ | XOR |
~expr | Unary bitwise complement (0s become 1s; 1s become 0s) |
<< | Shift left |
>> | Shift right |
用例:
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
条件表达式
dart有两个操作符用来简明地形容一个表达式,它们或许可以取代if-else
语句:
condition ? expr1 : expr2
这个是三元运算符,和java等常用语言一样的。
expr1 ?? expr2
如果expr1
是不为空,则表达式的值为expr1
,否则为expr2
.
由于三元运算符属于表达式而非语句,它们可以用在变量或者常量声明上:
var visibility = isPublic ? 'public' : 'private';
一般的判空取默认值操作可以使用??
操作符:
String playerName(String name) => name ?? 'Guest';
及联符号..
及联符号的出现优雅了构建器模式的概念。一个类有多个属性可以设置,往往需要声明一个临时变量,并连续使用这个临时变量进行set操作。及联符号可以省去这个临时变量,而让代码看起来像构建器模式一样:
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..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'); // Error: method 'write' isn't defined for 'void'.
这里的sb.write()
方法是一个void
类型的方法。他没有返回一个真实的对象。所以不能在此之上使用及联。
准确来说
..
运算符并不是一个运算符,它只是Dart语法的一部分。
其他操作符
你已经在其他的例子中看见过剩下的操作符了:
Operator | Name | Meaning |
---|---|---|
() |
Function application | 代表调用一个函数 |
[] |
List access | 获取中括号内索引指定的集合元素 |
. |
Member access | 访问一个表达式的成员,比如foo.bar 就是访问表达式foo 的成员bar
|
?. |
Conditional member access | 类似. ,但这个操作符多数用于左值是一个可能为空的值。比如foo?.bar 如果此时表达式foo 的返回值为null ,那么这个表达式整体为null 值,否则为bar 的值 |
这里有一个小技巧文档上没有讲:可以使用
foo?.bar??defaultValue
的形式来连续简化代码。