Flutter系列文章目录导读:
(一)Flutter学习之Dart变量和类型系统
(二)Flutter学习之Dart展开操作符 和 Control Flow Collections
(三)Flutter学习之Dart函数
(四)Flutter学习之Dart操作符、控制流和异常处理
(五)Flutter学习之Dart面向对象
(六)Flutter学习之Dart异步操作详解
(七)Flutter 学习之开发环境搭建
(八)Flutter 和 Native 之间的通信详解
(九)Android 项目集成 Flutter 模块
(十)Flutter FutureBuilder 优雅构建异步UI
更新中…
Dart 操作符和主流语言的操作符类似, 只要有一门常用语言, 对 Dart 掌握也是很快的
Dart 和以前介绍的 Kotlin 类似, 也提供操作符重载功能
Dart 支持下面常用的数学操作符:
操作符 | 意义 |
---|---|
+ | 加号 |
- | 减号 |
-expr | 一元操作符,负号 |
* | 乘号 |
/ | 除号 |
~/ | 除号,返回一个整型 |
% | 取余 |
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
需要注意的是两个整型相除是返回一个 double
类型的值,而不是像 Java 一样返回一个整型,如果需要返回整型可以使用 ~/
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; // 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
操作符 | 意义 |
---|---|
== | 相等 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
需要注意的是,比较断两个对象内容是否相等使用 ==
, 比较两个对象是否是同一个对象使用 identical()
函数
比较两个对象内容是否相等,需要重载操作符 ==
, 如何重载操作符后面会介绍
操作符 | 意义 |
---|---|
as | 类型强转 |
is | 判断某个对象是特定类型 |
is! | 判断某个对象不是特定类型 |
// 判断 emp 是否是 Person 类型
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
// 将 emp 强制转换为 Person, 如果 emp 不是 Person 类型则会抛出异常
(emp as Person).firstName = 'Bob';
操作符 | 意义 |
---|---|
!expr | 布尔取反 |
|| | 逻辑或 |
&& | 逻辑与 |
if (!done && (col == 0 || col == 3)) {
// ...Do something...
}
操作符 | 意义 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~expr | 按位非 |
<< | 左移 |
>> | 右移 |
操作符 | 意义 |
---|---|
= | 赋值(a = b) |
–= | a -= b 和 a = a - b 等价 |
/= | a /= b 和 a = a / b 等价 |
%= | a %= b 和 a = a % b 等价 |
>>= | a >>= b 和 a = a >> b 等价 |
^= | a ^= b 和 a = a ^ b 等价 |
+= | a += b 和 a = a + b 等价 |
*= | a *= b 和 a = a * b 等价 |
~/= | a ~/= b 和 a = a ~/ b 等价 |
<<= | a <<= b 和 a = a << b 等价 |
&= | a &= b 和 a = a & b 等价 |
|= | a |= b 和 a = a | b 等价 |
Dart 提供了两个操作符来简化特定情况的 if-else
语句
condition ? expr1 : expr2
如果 condition
是 true, 返回 expr1
, 否则返回 expr2
var visibility = isPublic ? 'public' : 'private';
expr1 ?? expr2
如果 expr1
不为 null, 返回 expr1
, 否则返回 expr2
String playerName(String name) => name ?? 'Guest';
例如我们可以把下面的函数使用条件表达式简化下:
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
// 可以简化成如下:
String playerName(String name) => name != null ? name : 'Guest';
String playerName(String name) => name ?? 'Guest';
严格的讲, 级联符号(cascade notation)
不是一个操作符, 而是 Dart 的一个语法糖, 它不仅可以让开发者链式调用函数, 还可以链式访问属性, 所以当我们需要频繁访问某个对象的属性和函数:
class Person {
int age = 0;
String name = "chiclaim";
void sayHello() {
print("hello , my name is $name");
}
}
main(){
var p = Person();
p.age = 1;
p.name = "johnny";
p.sayHello();
}
我们可以使用级联操作符改造成如下形势:
main() {
Person()
..age = 1
..name = "johnny"
..sayHello();
}
除此以外, 还可以级联嵌套, 如:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = '[email protected]'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
需要注意的是, 不能在一个 void
函数后面开始你的级联操作:
var sb = StringBuffer();
sb.write('foo')
..write('bar');
但是可以在调用 StringBuffer
构造函数后面开始你的级联操作:
StringBuffer()
..write('foo')
..write('bar');
操作符 | 意义 |
---|---|
() | 函数调用 |
[] | 集合元素访问 |
. | 成员访问 |
?. | 成员访问, 左边的操作符可以为null, foo?.bar 如果 foo 为空那么整个表达式为null |
Dart 提供操作符重载功能,允许开发者重载一下操作符:
操作符 | 操作符 | 操作符 | 操作符 |
---|---|---|---|
< | + | | | [] |
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
– | % | >> |
操作符重载的语法格式为: 返回类型 operator
操作符 (参数)
下面来看下官方一个操作符重载的例子:
class Vector {
final int x, y;
Vector(this.x, this.y);
// 重载 + 操作符
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
// 重载 - 操作符
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
// 重载 == 操作符
bool operator ==(other) => other is Vector
&& runtimeType == other.runtimeType
&& x == other.x && y == other.y;
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
如果我们重载了 == 操作符, 也要重载 hashCode
函数, 类似 Java 中重载了 equals
函数,最好也重载 hashCode
函数是一样的, 因为对象的 hash
值决定对象的存储位置
@override
int get hashCode => x.hashCode ^ y.hashCode;
Dart 支持 if 语句和可选的 else 语句
if (isRaining()) {
print("raining");
} else if (isSnowing()) {
print("snowing");
} else {
print("unknown");
}
Dart 不仅支持标准的 for 循环:
for (var i = 0; i < 5; i++) {
}
还支持 for-in
那些实现了 Iterator
接口的类(如List/Set):
var collection = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
通过上一篇文章(三)Flutter学习之Dart函数的介绍知道
Dart Closures
能够访问自身作用域内的变量, 哪怕这个变量是外部传递给 Closure 的 如:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
// 输出
1
2
但是在 JavaScript
中会输出两个 2
while/do-while/break/continue
和其他语言没有什么区别, 在这里就不赘述了
Switch 语句可以接受 整型、字符串 、枚举
或者 编译时常量
, 然后使用 ==
进行比较, 下面看下常量的比较:
class Person {
final String id;
const Person(this.id);
}
const p = Person("001");
const p1 = Person("001");
const p2 = Person("003");
switch (p) {
case p1:
print(p1.id);
break;
case p2:
print(p2.id);
break;
default:
print("unknown");
}
如果 case
子句不是空, 要以 break/return/throw/continue
结尾, 否则会报错
如果想要 case
子句之间 fall-through
的话, 将 case
子句为空即可
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
case 'NOW_CLOSED':
print("NOW_CLOSED");
break;
default:
print("UNKNOWN");
}
还可以使用 continue
的方式 fall-through
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
print("CLOSED");
continue nowClosed;
nowClosed:
case 'NOW_CLOSED':
print("NOW_CLOSED");
break;
default:
print("UNKNOWN");
}
// 输出
CLOSED
NOW_CLOSED
在开发阶段, 我们可以使用断言语句 assert(condition, optionalMessage);
来中断程序的正常执行, 当 condition
为 false
的时候(抛出 AssertionError
异常); 如果 condition
为 true
, 则继续执行程序的下一行代码. 例如:
// Make sure the value is less than 100.
assert(number < 100);
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
断言什么时候生效呢?这取决于你使用的工具和框架:
debug
模式启用断言dartdevc
, 默认是开启断言dart
、dart2js
等工具支持命令行来启用断言: --enable-asserts
在生产环境的代码, 断言语句将会被忽略, 断言的 condition
表达式不会被执行,所以不用担心性能问题
Dart 提供了 throw, rethrow, try, catch, on, finally
关键字让开发者能够抛出和捕获异常
和 Java
不同的是, Dart 中所有的异常都是 unchecked
异常, 也就是说编译器不会强制开发者去捕获任何异常, 除非你有这个需要
Dart 提供了两个类异常: Exception
和 Error
, Dart 不仅可以抛出异常还可以抛出任何不为 null
的对象:
// 抛出异常
throw FormatException('Expected at least 1 section');
// 抛出不为 null 的对象
throw 'Out of llamas!';
虽然 Dart 允许我们抛出一个不为 null 的普通对象,但是官方还是建议我们抛出的异常继承自 Exception
或 Error
介绍完了 throw
关键字,我们来看下 catch
和 on
和 关键字:
catch
和 on
都是用来捕获异常:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// 捕获一个特定的异常
buyMoreLlamas();
} on Exception catch (e) {
// 捕获所有继承自 Exception 的异常,并拿到异常对象
print('Unknown exception: $e');
} catch (e) {
// 捕获所有异常
print('Something really unknown: $e');
}
可见, on
关键字用于指定捕获特定的异常, catch
关键字用于拿到异常对象
catch
关键字除了可以拿到异常对象, 还可以拿到异常的 堆栈
信息, 如:
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
一般情况下, 使用了 on, catch
关键字来捕获异常, 异常会停止传播, 如果需要异常继续传播可以使用 rethrow
关键字
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
最后介绍 Dart
异常处理的最后一个关键字 finally
finnaly
关键字很简单 , 就是不管是否抛出异常 finally
子句一定会执行:
try {
breedMoreLlamas();
} finally {
// 就算抛出异常(程序中断执行), finnaly 也会先执行
cleanLlamaStalls();
}
try {
breedMoreLlamas();
} catch (e) {
// 捕获异常
print('Error: $e');
} finally {
// 执行 finally 子句
cleanLlamaStalls(); // Then clean up.
}
关于 Dart
的 操作符, 控制流, 异常处理
就介绍完毕
https://dart.dev/guides/language/language-tour#operators
https://medium.com/@ayushpguptaapg/demystifying-and-hashcode-in-dart-2f328d1ab1bc
另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图。
如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图: